1. 一次性初始化:
互斥量智能初始化一次,再次初始化会出现错误。比如自己写库函数时将互斥量封装进去,这时一次性初始化就用上场了。
定义变量pthread_once_t once_control = PTHREAD_ONCE_INIT;
void init_routine(){
//初始化互斥量
//初始化读写锁
}
接下来在函数pthread_once引用上述变量和函数,多线程只会执行函数指针一次。
2. 线程属性
创建线程时可以使用pthread_attr_t类型参数变更属性。
如:
a. detachstate线程的分离状态
b. guardsize线程栈末尾的警戒区域大小//预防栈溢出
c. stackaddr线程栈的最低地址
d. stacksize线程栈的大小//不能小于最小值
并不是全部线程都支持这些熟悉,修改前需要先检查。
pthread_attr_setstack //修改栈地址、大小
pthread_attr_getstack //获取栈属性
pthread_attr_setstacksize //设置大小,不能设地址
pthread_attr_sgetstacksize
3. 线程的同步属性
1). 互斥量的属性:
a.进程共享属性:PTHREAD_PROCESS_PRIVATE,同一进程的多个线程访问权限;PTHREAD_PROCESS_SHARED:多个进程访问权限。
如果互斥量在多进程的共享内存区域,那么具有这个属性的互斥量可以同步多进程。
pthread_mutexattr_getpshared/pthread_mutexattr_setshared设置、获取互斥量进程共享属性。
b.类型属性:设置互斥量的类型属性,PTHREAD_MUTEX_NORMAL等;
pthread_mutexattr_settype/pthread_mutexattr_gettype设置、查看出现
2).读写锁属性
3)条件变量属性
4. 示例,互斥量属性操作
#include "apue.h"
int main()
{
char *shm = "myshm";
char *shm1 = "myshm1";
int shm_id, shm_id1;
char *buf;
pid_t pid;
pthread_mutex_t *mutex;
pthread_mutexattr_t mutexattr;
//打开共享内存
shm_id1 = shm_open(shm1, O_RDWR|O_CREAT, 0644);
//调整共享内存大小
ftruncate(shm_id1, 100);
//映射共享内存,MAP_SHARED属性表明,对共享内存的任何修改都会影响其他进程
mutex =(pthread_mutex_t *)mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, shm_id1, 0);
pthread_mutexattr_init(&mutexattr);
#ifdef _POSIX_THREAD_PROCESS_SHARED
pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
#endif
pthread_mutex_init(mutex, &mutexattr);
//打开共享内存
shm_id = shm_open(shm, O_RDWR|O_CREAT, 0644);
//调整共享内存大小
ftruncate(shm_id, 100);
//映射共享内存,MAP_SHARED属性表明,对共享内存的任何修改都会影响其他进程
buf =(char *)mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, shm_id, 0);
pid = fork();
if(pid==0)
{
//休眠1s,让父进程先运行
sleep(1);
printf("I'm child proccess\n");
pthread_mutex_lock(mutex);
//将共享内存内存修改为hello
memcpy(buf, "hello", 6);
printf("child buf is : %s\n", buf);
pthread_mutex_unlock(mutex);
}
else if(pid>0)
{
printf("I'm parent proccess\n");
pthread_mutex_lock(mutex);
//修改共享内存到内容,改为world
memcpy(buf, "world", 6);
sleep(3);
printf("parent buf is : %s\n", buf);
pthread_mutex_unlock(mutex);
}
pthread_mutexattr_destroy(&mutexattr);
pthread_mutex_destroy(mutex);
//解除映射
munmap(buf, 100);
//消除共享内存
shm_unlink(shm);
//解除映射
munmap(mutex, 100);
//消除共享内存
shm_unlink(shm1);
}
5. 线程私有数据
多个函数多个进程可以访问这个变量,看起来像全局变量;但是它又不是全局变量,线程对这个变量的访问不会彼此产生影响。比如参数errno。
使用私有数据前,要先创建一个与私有数据相关的key,类型pthread_key_t.
使用函数pthread_key_create创建键,创建的键放在参数key指向的内存单元,另一个参数destructor是析构函数。
pthread_key_delete销毁键,当键销毁后与它关联的数据并没有被销毁!
pthread_setspecific将私有数据与key关联
pthread_getspecific获取私有数据的地址
6. 示例,使用私有数据
/*DATE: 2015-4-17
*AUTHOR: WJ
*DESCRIPTION: 线程到私有数据, 一个像errno一样到数据
*/
#include "apue.h"
pthread_key_t key;
void *thread_fun1(void *arg)
{
printf("thread 1 start!\n");
int a = 1;
//将a和key关联
pthread_setspecific(key, (void *)a);
sleep(2);
printf("thread 1 key->data is %d\n", pthread_getspecific(key));
}
void *thread_fun2(void *arg)
{
sleep(1);
printf("thread 2 start!\n");
int a = 2;
//将a和key关联
pthread_setspecific(key, (void *)a);
printf("thread 2 key->data is %d\n", pthread_getspecific(key));
}
int main()
{
pthread_t tid1, tid2;
//创造一个key
pthread_key_create(&key, NULL);
//创造新线程
if(pthread_create(&tid1, NULL, thread_fun1, NULL))
{
printf("create new thread 1 failed\n");
return;
}
if(pthread_create(&tid2, NULL, thread_fun2, NULL))
{
printf("create new thread 2 failed\n");
return;
}
//等待新线程结束
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_key_delete(key);
return;
}
7. 线程与fork
当线程fork时,为子进程创建了整个进程地址空间的副本,子进程通过继承整个地址空间的副本,也会将父进程的互斥量、读写锁、条件变量的状态继承过来。如果父进程中互斥量是锁着的,那么子进程中互斥量也是锁着的,虽然子进程自己还没lock。这时,子进程无法解锁。
子进程内部只有一个线程,由父进程中调用fork函数的线程副本构成。如果调用fork的线程将互斥量锁住,那么子进程会拷贝一个pthread_mutex_lock副本。这时子进程就有机会解锁。
pthread_atfork函数
参数prepare在fork调用前会被调用
参数parent在fork返回父进程前调用
参数child在fork返回子进程前调用
8. 示例,线程与fork安全
/*DATE: 2015-4-17
*AUTHOR: DDDDD
*DESCRIPTION: 安全的使用fork
* prepare 在调用fork之前会被调用
* parent 在fork返回父进程之前被调用
* child 在fork返回子进程之前被调用
*/
#include "apue.h"
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void prepare()
{
pthread_mutex_lock(&mutex);
printf("I'm prepare\n");
}
void parent()
{
pthread_mutex_unlock(&mutex);
printf("I'm parent\n");
}
void child()
{
pthread_mutex_unlock(&mutex);
printf("I'm child\n");
}
void *thread_fun(void *arg)
{
sleep(1);
pid_t pid;
pthread_atfork(prepare, parent, child);
pid = fork();
if(pid==0)
{
pthread_mutex_lock(&mutex);
printf("child process\n");
pthread_mutex_unlock(&mutex);
}
if(pid>0)
{
pthread_mutex_lock(&mutex);
printf("parent process\n");
pthread_mutex_unlock(&mutex);
}
}
int main()
{
pthread_t tid;
if(pthread_create(&tid, NULL, thread_fun, NULL))
{
printf("create new thread failed\n");
return;
}
pthread_mutex_lock(&mutex);
sleep(2);
printf("main\n");
pthread_mutex_unlock(&mutex);
pthread_join(tid, NULL);
pthread_mutex_destroy(&mutex);
return;
}
注意:线程安全是gcc -pthread 编译
线程非安全是gcc-lpthread编译