单核cpu需要对多线程访问共享资源加锁吗
需要。时间片完,也会引起线程的调度,需要对共享资源加锁。
线程参考
#includeint pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
参数:
thread: 传出参数,是无符号长整形数,线程创建成功,会将线程 ID 写入到这个指针指向的内存中
attr: 线程的属性,一般情况下使用默认属性即可,写 NULL
start_routine: 函数指针,创建出的子线程的处理动作,也就是该函数在子线程中执行。
arg: 作为实参传递到 start_routine 指针指向的函数内部
返回值:线程创建成功返回 0,创建失败返回对应的错误号
#include#include #include #include #include void *working(void *args){ printf("i am child thread%ld",pthread_self()); for(int i=0;i<10;i++) printf("num:%dn", i); return NULL; } int main(){ pthread_t tid; pthread_create(&tid,NULL,working,NULL); printf("thread create %ldn",tid); // printf("i am father thread %ldn",pthread_self()); for(int i=0;i<3;i++) printf("num:%dn",i); //sleep(1); return 0; }
输出
thread create 139716524607232 i am father thread 139716524611392 num:0 num:1 num:2
子线程的并不输出,原因:
子线程被创建出来后,抢cpu时间片,抢不到就不运行。在子线程运行期间如果主线程退出,子线程会一并被销毁。
因为子线程使用的是主线程的虚拟地址空间。
解决方案:让子线程执行完毕,主线程再退出,可以在主线程中添加挂起函数 sleep();
进一步:
加上这行 主线程调用退出函数退出, 地址空间不会被释放
pthread_exit(NULL);
#include#include #include #include #include void *working(void *args){ printf("i am child thread%ld",pthread_self()); for(int i=0;i<10;i++) printf("num:%dn", i); return NULL; } int main(){ pthread_t tid; pthread_create(&tid,NULL,working,NULL); printf("thread create %ldn",tid); // printf("i am father thread %ldn",pthread_self()); for(int i=0;i<3;i++) printf("num:%dn",i); 加上这行 主线程调用退出函数退出, 地址空间不会被释放 pthread_exit(NULL); return 0; }
pthread_exit()
线程退出但是不会导致虚拟地址空间的释放,针对于主线程,可以使用pthread_exit();
#includevoid pthread_exit(void *retval);
参数:线程退出的时候携带的数据,当前子线程的主线程会得到该数据。如果不需要使用,指定为 NULL
线程回收 子线程与主线程之间的通信子线程退出的时候其内核资源由主线程回收
pthread_join();子线程还在运行则改函数阻塞直到子线程完成,调用一次回收一个,回收所有需要循环进行回收。第二个参数获取传递出来的数据(由pthread_exit(void** retvl)传出来的返回值)。
#include#include #include #include #include void *working(void *args) { int num=2; pthread_exit(&num); return NULL; } int main() { pthread_t tid[10]; for(int i=0;i<10;i++){ pthread_create(&tid[i], NULL, working, NULL); printf("thread create %ldn", tid[i]); } for(int i=0;i<10;i++){ void *N=NULL; pthread_join(tid[i],&N); printf("N:%dn",*(int*)N); } // printf("i am father thread %ldn", pthread_self()); for (int i = 0; i < 3; i++) printf("num:%dn", i); pthread_exit(NULL); return 0; }
输出:
N:32747 N:32767 N:32767 N:32767 N:32767 N:32767 N:32767 N:32767 N:32767 N:32767
数据保存在子线程自己的栈区,因为子线程销毁了,子线程的栈空间也归还给主线程,因此获取不到。
使用全局变量:#include#include #include #include #include int num=1; void *working(void *args) { num*=2; pthread_exit(&num); return NULL; } int main() { pthread_t tid[10]; for(int i=0;i<10;i++){ pthread_create(&tid[i], NULL, working, NULL); printf("thread create %ldn", tid[i]); } for(int i=0;i<10;i++){ void *N=NULL; pthread_join(tid[i],&N); printf("N:%dn",*(int*)N); } // printf("i am father thread %ldn", pthread_self()); for (int i = 0; i < 3; i++) printf("num:%dn", i); pthread_exit(NULL); return 0; }
thread create 140322816030464 thread create 140322807637760 thread create 140322799245056 thread create 140322790741760 thread create 140322782349056 thread create 140322773956352 thread create 140322765563648 thread create 140322688464640 thread create 140322680071936 thread create 140322671679232 N:512 N:512 N:512 N:512 N:1024 N:1024 N:1024 N:1024 N:1024 N:1024 i am father thread 140322816034624 num:0 num:1 num:2
访问的是共享变量。void可以使用任意类型数据 void*为数据地址,void为指向数据地址的指针。
使用主线程的栈创建子线程的时候传参数
#include#include #include #include #include void *working(void *args) { int *num=(int*)args; *num*=2; pthread_exit(num); return NULL; } int main() { int num=1; pthread_t tid[10]; for(int i=0;i<10;i++){ pthread_create(&tid[i], NULL, working, &num); printf("thread create %ldn", tid[i]); } for(int i=0;i<10;i++){ void *N=NULL; pthread_join(tid[i],&N); printf("N:%dn",*(int*)N); } // printf("i am father thread %ldn", pthread_self()); for (int i = 0; i < 3; i++) printf("num:%dn", i); pthread_exit(NULL); return 0; }
N:512 N:512 N:512 N:512 N:1024 N:1024 N:1024 N:1024 N:1024 N:1024线程分离
一般情况下,主线程有自己的业务逻辑,如果主线程负责子线程的资源回收,调用pthread_join()只要子线程不退出,主线程就会一直被阻塞加粗样式,主线程就也被阻塞。
pthread_detach()线程分离函数,子线程就可以和主线程分离,当子线程退出的时候,其占用的内核资源就被系统的其他进程接管并回收了。线程分离之后在主线程中使用 pthread_join() 就回收不到子线程资源了。
#include线程取消#include #include #include #include void *working(void *args) { printf("i am child thread%ld", pthread_self()); sleep(3); for (int i = 0; i < 10; i++) printf("num:%dn", i); return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, working, NULL); printf("thread create %ldn", tid); pthread_detach(tid); // printf("i am father thread %ldn", pthread_self()); for (int i = 0; i < 3; i++) printf("num:%dn", i); pthread_exit(NULL); return 0; }
在一个线程中杀死另一个线程。使用这个函数杀死一个线程需要分两步
1.A中调用pthread_cancel指定杀死线程B,B不死
2.B进行一次系统调用,被杀死
#include// 参数是子线程的线程ID int pthread_cancel(pthread_t thread);
#include#include #include #include #include void *working(void *args) { printf("i am child thread%ld", pthread_self()); sleep(10); for (int i = 0; i < 10; i++) printf("num:%dn", i); return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, working, NULL); printf("thread create %ldn", tid); pthread_cancel(tid); // printf("i am father thread %ldn", pthread_self()); for (int i = 0; i < 3; i++) printf("num:%dn", i); pthread_exit(NULL); return 0; }
子线程中cout进行了系统调用
thread create 140093522048768 i am child thread140093522048768i am father thread 140093522052928 num:0 num:1 num:2线程 ID 比较
在 Linux 中线程 ID 本质就是一个无符号长整形,因此可以直接使用比较操作符比较两个线程的 ID,但是线程库是可以跨平台使用的,在某些平台上 pthread_t 可能不是一个单纯的整形,这中情况下比较两个线程的 ID 必须要使用比较函数,函数原型如下:
#includeint pthread_equal(pthread_t t1, pthread_t t2);
参数:t1 和 t2 是要比较的线程的线程 ID
返回值:如果两个线程 ID 相等返回非 0 值,如果不相等返回 0
c++11后增加了线程类
std::thread
- 构造函数
// ① thread() noexcept; // ② thread( thread&& other ) noexcept; // ③ template< class Function, class... Args > explicit thread( Function&& f, Args&&... args ); // ④ thread( const thread& ) = delete;
1.构造函数:默认构造函数,构造一个线程对象,不执行任何处理动作
2.移动构造函数,把other的线程所有权转移给新的thread对象,之后other不再表示执行线程
3.创建线程对象,并在该线程中执行函数f中的业务逻辑,args是参数
f可以是,普通函数,类成员函数,匿名函数,仿函数,也可以是可调用对象包装器类型。
4.使用delete显示删除拷贝构造,不允许线程对象之间的拷贝。
成员函数:
- get_id() 获取线程id
std::thread::id get_id() const noexcept;
#include#include #include using namespace std; void func(int num,string str){ for(int i=0;i<10;i++){ cout<<"child thread i="< 线程分离函数 detach () 不会阻塞线程,子线程和主线程分离之后,在主线程中就不能再对这个子线程做任何控制了,比如:通过 join () 阻塞主线程等待子线程中的任务执行完毕,或者调用 get_id () 获取子线程的线程 ID。有利就有弊,鱼和熊掌不可兼得,建议使用 join ()。
joinable()
joinable() 函数用于判断主线程和子线程是否处理关联(连接)状态,一般情况下,二者之间的关系处于关联状态,该函数返回一个布尔类型:
返回值为 true:主线程和子线程之间有关联(连接)关系
返回值为 false:主线程和子线程之间没有关联(连接)关系#include#include #include using namespace std; void foo(){ this_thread::sleep_for(std::chrono::seconds(1)); } int main(){ thread t; cout<<"before starting joinable"< //输出 0 1 0 1 0创建时,未指定任务函数,子线程不启动也不绑定。启动任务时,自动绑定。join()任务完成,子线程自动销毁也自动解除绑定。detach()父子线程分离,也就解除绑定。
operator
线程中的资源是不能被复制的,因此通过 = 操作符进行赋值操作最终并不会得到两个完全相同的对象// move (1) thread& operator= (thread&& other) noexcept; // copy [deleted] (2) thread& operator= (const other&) = delete;通过以上 = 操作符的重载声明可以得知:
如果 other 是一个右值,会进行资源所有权的转移
如果 other 不是右值,禁止拷贝,该函数被显示删除(=delete),不可用



