pthread_create第一个参数(pthread create)
为什么pthread_join的第一个参数是线程id而不是指针
pthread_t thdId; //这种类型就是一个整形类型,thdId就是一个整形变量,pthread_create第一参数是指针那是为了能够给thdId赋值,不用指针,赋值的话,函数结束的话,thdId的值传不出来。这是C语言的内容。函数的传参。ID类型的都是整形变量。不是指针。
c++ 多线程编程常用的几个函数
1、C++多线程也可以使用UNIX C的库函数,pthread_mutex_t,pthread_create,pthread_cond_t,pthread_detach,pthread_mutex_lock/unlock,等等。在使用多线程的时候,你需要先创建线程,使用pthread_create,你可以使主线程等待子线程使用pthread_join,也可以使线程分离,使用pthread_detach。线程使用中最大的问题就是同步问题,一般使用生产着消费者模型进行处理,使用条件变量pthread_cond_t,pthread_mutex,pthread_cond_wait来实现。
2、例程:
//创建5个线程
#include pthread.h
#include stdlib.h
void* work_thread(void* arg)
{
//线程执行体
return 0;
}
int main(int argc,char* argv[])
{
int nthread = 5;//创建线程的个数
pthread_t tid;//声明一个线程ID的变量;
for(int i=0;inthread;i++)
{
pthread_create(tid,NULL,work_thread,NULL);
}
sleep(60);//睡眠一分钟,你可以看下线程的运行情况,不然主进程会很快节结束了。
}
pthread_create(tid,NULL,work_thread,NULL);//创建线程的函数,第一个参数返回线程的ID;第二个参数是线程的属性,一般都置为NULL;第三个参数是线程函数,线程在启动以后,会自动执行这个函数;第四个参数是线程函数的参数,如果有需要传递给线程函数的参数,可以放在这个位置,可以是基础类型,如果你有不止一个参数想传进线程函数,可以做一个结构体,然后传入。
线程特有数据(Thread Specific Data)
在单线程程序中,我们经常要使用 全局变量 来实现多个函数间共享数据。在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时在应用程序设计中有必要提供 线程私有 的全局变量,仅在某个线程中有效,但可以跨多个函数访问,这样每个线程访问它自己独立的数据空间,而不用担心和其它线程的同步访问。
这样在一个线程内部的各个函数都能访问、但其它线程不能访问的变量,我们就需要使用 线程局部静态变量 (Static memory local to a thread) 同时也可称之为 线程特有数据 (Thread-Specific Data 或 TSD),或者 线程局部存储 (Thread-Local Storage 或 TLS)。
POSIX 线程库提供了如下 API 来管理线程特有数据(TSD):
第一参数 key 指向 pthread_key_t 的对象的指针。请 注意 这里 pthread_key_t 的对象占用的空间是用户事先分配好的, pthread_key_create 不会动态生成 pthread_key_t 对象。
第二参数 desctructor ,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。
有时我们在线程里初始化时,需要避免重复初始化。我们希望一个线程里只调用 pthread_key_create 一次,这时就要使用 pthread_once 与它配合。
第一个参数 once_control 指向一个 pthread_once_t 对象,这个对象必须是常量 PTHREAD_ONCE_INIT ,否则 pthread_once 函数会出现不可预料的结果。
第二个参数 init_routine ,是调用的初始化函数,不能有参数,不能有返回值。
如果成功则返回0,失败返回非0值。
创建完键后,必须将其与线程数据关联起来。关联后也可以获得某一键对应的线程数据。关联键和数据使用的函数为:
第一参数 key 指向键。
第二参数 value 是欲关联的数据。
函数成功则返回0,失败返回非0值。
注意: 用 pthread_setspecific 为一个键指定新的线程数据时,并不会主动调用析构函数释放之前的内存,所以调用线程必须自己释放原有的线程数据以回收内存。
获取与某一个键关联的数据使用函数的函数为:
参数 key 指向键。
如果有与此键对应的数据,则函数返回该数据,否则返回NULL。
删除一个键使用的函数为:
参数 key 为要删除的键。
成功则返回0,失败返回非0值。
注意: 该函数将键设置为可用,以供下一次调用 pthread_key_create() 使用。它并不检查当前是否有线程正在使用该键对应的线程数据,所以它并不会触发函数 pthread_key_create 中定义的 destructor 函数,也就不会释放该键关联的线程数据所占用的内存资源,而且在将 key 设置为可用后,在线程退出时也不会再调用析构函数。所以在将 key 设置为可用之前,必须要确定:
在 Linux 中每个进程有一个全局的数组 __pthread_keys ,数组中存放着 称为 key 的结构体,定义类似如下:
在 key 结构中 seq 为一个序列号,用来作为使用标志指示这个结构在数组中是否正在使用,初始化时被设为0,即表示 不在使用 。 destructor 用来存放一个析构函数指针。
pthread_create_key 会从数组中找到一个还未使用的 key 元素,将其序列号 seq 加1,并记录析构函数地址,并将 key 在数组 __pthread_keys 中的 下标 作为返回值返回。那么如何判断一个 key 正在使用呢?
如果 key 的序列号 seq 为偶数则表示未分配,分配时将 seq 加1变成奇数,即表示正在使用。这个操作过程采用原子 CAS 来完成,以保证线程安全。在 pthread_key_delete() 时也将序列号 seq 加1,表示可以再被使用,通过序列号机制来保证回收的 key 不会被复用(复用 key 可能会导致线程在退出时可能会调用错误的析构函数)。但是一直加1会导致序列号回绕,还是会复用 key ,所以调用 pthread_create_key 获取可用的 key 时会检查是否有回绕风险,如果有则创建失败。
除了进程范围内的 key 结构数组外,系统还在进程中维护关于每个线程的控制块 TCB(用于管理寄存器,线程栈等),里面有一个 pthread_key_data 类型的数组。这个数组中的元素数量和进程中的 key 数组数量相等。 pthread_key_data 的定义类似如下:
根据 pthread_key_create() 返回的可用的 key 在 __pthread_keys 数组中的下标, pthread_setspecific() 在 pthread_key_data 的数组 中定位相同下标的一个元素 pthread_key_data ,并设置其序号 seq 设置为对应的 key 的序列号,数据指针 data 指向设置线程特有数据(TSD)的值。
pthread_getspecific() 用于将 pthread_setspecific() 设置的 data 取出。
线程退出时, pthread_key_data 中的序号 seq 用于判断该 key 是否仍在使用中(即与在 __pthread_keys 中的同一个下标对应的 key 的序列号 seq 是否相同),若是则将 pthread_key_data 中 data(即 线程特有数据 TSD)作为参数调用析构函数。
由于系统在每个进程中 pthread_key_t 类型的数量是有限的,所有在进程中并不能获取无限个 pthread_key_t 类型。Linux 中可以通过 PTHREAD_KEY_MAX(定义于 limits.h 文件中)或者系统调用 sysconf(_SC_THREAD_KEYS_MAX) 来确定当前系统最多支持多少个 key 。 Linux 中默认是 1024 个 key,这对大多数程序来书已经够了。如果一个线程中有多个线程局部存储变量(TLS),通常可以将这些变量封装到一个数据结构中,然后使用封装后的数据结构和一个线程局部变量相关联,这样就能减少对键值的使用。