内容:
1,什么是一次性初始化?
例子1:pthread_once函数的一次性初始化验证:
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
pthread_once_t once = PTHREAD_ONCE_INIT;//声明全局变量,确保在本进程中即被执行一次
pthread_t tid;//全局的线程id变量
//可以在该函数里初始化互斥量,读写锁
//这里不做任何初始化,仅仅打印是哪个线程调用了它
void init_routine()
{
printf("init_routine used by thread 0x%ld\n",tid);
}
//线程1
void *pthread_fun1(void *arg)
{
tid = pthread_self();//获取自身tid
printf("this is thread1 0x%ld\n",tid);
printf("once 1 is %ld\n",once);
pthread_once(&once,init_routine);//调用初始化函数
printf("once 1 is %ld\n",once);
sleep(1);
return (void*)1;
}
void *pthread_fun2(void *arg)
{
sleep(2);
tid = pthread_self();//获取自身tid
printf("this is thread2 0x%ld\n",tid);
printf("once 2 is %ld\n",once);
pthread_once(&once,init_routine);//调用初始化函数
printf("once 2 is %ld\n",once);
sleep(1);
return (void*)2;
}
int main()
{
pthread_t tid1,tid2;
int errno1,errno2;
int jval_1,jval_2;
int *rval_1,*rval_2;
errno1=pthread_create(&tid1,NULL,pthread_fun1,NULL);
if(errno1 !=0)
{
printf("create new thread1 failure\n");
return errno1;
}
errno2=pthread_create(&tid2,NULL,pthread_fun2,NULL);
if(errno2 !=0)
{
printf("create new thread2 failure\n");
return errno2;
}
sleep(1);
jval_1=pthread_join(tid,&rval_1);//等待新线程1执行完成1
sleep(1);
jval_2=pthread_join(tid,&rval_2);//等待新线程2执行完成
sleep(2);
printf("jval_1=%d\n",jval_1);//连接线程1的函数返回值
printf("jval_2=%d\n",jval_2);//连接线程2的函数返回值
printf("rval_1=%d\n",(int*)rval_1);//线程1的返回值
printf("rval_2=%d\n",(int*)rval_2);//线程2的返回值
return 0;
}
运行结果如下图:
1)当once_control值为PTHREAD_ONCE_INIT时
如结果所示,在新线程thread1中pthread_once()函数调用初始化函数init_routine()进行初始化,当init_routine()被thread1调用之前值为0调用之后值为2,once的值变为2即不能再被其他线程调用(一次性初始化的功能);再等到线程2调用pthread_once函数时,此时once值为2已经无法调用初始化函数init_routine来为其线程做初始化操作了。
2)当once_control值为1时,
拥有pthread_once函数的线程thread1和thread2陷入永久等待,无法执行init_routine函数。
2,线程的属性
线程的几种属性
1)线程属性初始化和销毁
2)分离属性
分离在线程结束时会释放内存所占的资源空间,不分离会产生僵尸线程。
分离属性的用法
例子2:分离属性的应用实例
建立thread_attr_detache.c;内容如下
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
//线程1
void *pthread_fun1(void *arg)
{
printf("this is thread1 0x%ld\n",pthread_self());
return (void*)1;
}
void *pthread_fun2(void *arg)
{
printf("this is thread2 0x%ld\n",pthread_self());
return (void*)2;
}
int main()
{
pthread_t tid1,tid2;
int errno1,errno2;
int jval_1,jval_2;
int *rval_1,*rval_2;
pthread_attr_t attr;//声明属性对象
pthread_attr_init(&attr);//初始化属性
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置属性
errno1=pthread_create(&tid1,&attr,pthread_fun1,NULL);
if(errno1 !=0)
{
printf("create new thread1 failure\n");
return errno1;
}
errno2=pthread_create(&tid2,NULL,pthread_fun2,NULL);
if(errno2 !=0)
{
printf("create new thread2 failure\n");
return errno2;
}
jval_1=pthread_join(tid1,&rval_1);//等待新线程1执行完成1
jval_2=pthread_join(tid2,&rval_2);//等待新线程2执行完成
printf("jval_1=%d\n",jval_1);//连接线程1的函数返回值
printf("jval_2=%d\n",jval_2);//连接线程2的函数返回值
printf("rval_1=%d\n",(int*)rval_1);//线程1的返回值
printf("rval_2=%d\n",(int*)rval_2);//线程2的返回值
pthread_attr_destroy(&attr); //销毁属性
return 0;
}
当线程属性attr设置为分离属性DETACHED时,将无法与该线程进行连接,如例中将线程tread1的属性设置为分离属性;此时主线程就无法与之建立连接。而线程2的属性默认为NULL即非分离状态,运行结果如下图:
将属性设置为JOINABLE时,即非分离,且可以成功连接,运行结果如下图:
可以用pthread_attr_gettachstate()函数来获取其线程的分离状态;在主线程调用该函数。
3)栈属性
线程的栈大小与地址(查看栈的大小的命令:ulimit -s)
栈尾禁戒区
栈属性实例:获取和设置栈属性的大小;
建立thread_attr_setstack.c;内容如下
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"
pthread_attr_t attr;
void *pthread_fun(void *arg)
{
size_t stacksize;
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
pthread_attr_getstacksize(&attr,&stacksize);
#endif
printf("this thread stack size is %ld\n",stacksize);
return (void*)2;
}
int main()
{
pthread_t tid;
int errno;
int jval;
int *rval;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
pthread_attr_setstacksize(&attr,PTHREAD_STACK_MIN);
#endif
errno=pthread_create(&tid,&attr,pthread_fun,NULL);
if(errno !=0)
{
printf("create new thread1 failure\n");
return errno;
}
jval=pthread_join(tid,&rval);//等待新线程执行完成
printf("jval=%d\n",jval);//连接线程1的函数返回值
printf("rval=%d\n",(int*)rval);//线程2的返回值
pthread_attr_destroy(&attr);
return 0;
}
- 如上代码通过pthread_attr_setstacksize设置栈大小为系统默认最小PTHREAD_STACK_MIN,运行结果如下:
如结果所示,默认最小值为16384
当我们不通过pthread_attr_setstacksize来设置其大小时,其默认值为最大值;我们也可以自己设置栈的大小,但不能小于其默认最小值,否则就会设置失败,设置失败时系统会自动默认设置为最大值;如我们设置为5000,修改如下:
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"
pthread_attr_t attr;
void *pthread_fun(void *arg)
{
size_t stacksize;
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
pthread_attr_getstacksize(&attr,&stacksize);
printf("first this thread stack size is %ld\n",stacksize);
pthread_attr_setstacksize(&attr,5000);
#endif
printf("first this thread stack size is %ld\n",stacksize);
return (void*)2;
}
int main()
{
pthread_t tid;
int errno;
int jval;
int *rval;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
//pthread_attr_setstacksize(&attr,PTHREAD_STACK_MIN);
#endif
errno=pthread_create(&tid,&attr,pthread_fun,NULL);
if(errno !=0)
{
printf("create new thread1 failure\n");
return errno;
}
jval=pthread_join(tid,&rval);//等待新线程执行完成
printf("jval=%d\n",jval);//连接线程1的函数返回值
printf("rval=%d\n",(int*)rval);//线程2的返回值
pthread_attr_destroy(&attr);
return 0;
}
其结果如下:
2,线程的同步属性
1)互斥量的属性
1,进程共享属性
2,类型属性
3,互斥量属性实例
将互斥量属性设置为进程共享属性,实现进程的同步;
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"
#include"signal.h"
#include"sys/shm.h"
int main()
{
char *shm="myshm";
char *shm1="myshm1";
int shm_id,shm_id1;
char *buf;
pid_t pid;
//打开共享内存
shm_id=shmget(IPC_PRIVATE,128,0777);
if(shm_id<0)
{
printf("create shared memory failure\n");
return -1;
}
//印射共享内存
buf =(char*)shmat(shm_id,NULL,0);
if(buf==NULL)
{
printf("reflect the shared memory failure\n");
return -2;
}
pthread_mutex_t *mutex;
pthread_mutexattr_t mutexattr;
//打开共享内存 again
shm_id1=shmget(IPC_PRIVATE,128,0777);
if(shm_id1<0)
{
printf("second create shared memory failure \n");
return -1;
}
//印射共享内存
mutex =(char*)shmat(shm_id,NULL,0);
if(mutex==NULL)
{
printf("reflect the shared memory to mutex failure\n");
return -2;
}
pthread_mutexattr_init(&mutexattr);
#ifdef _POSIX_THREAD_PROCESS_SHARED
pthread_mutexattr_setphared(&mutexattr,PTHREAD_PROCESS_SHARED);
#endif
pthread_mutex_init(mutex,&mutexattr);
pid=fork();
if(pid==0)
{
//睡眠1S,认出cpu给父进程先运行
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);
}
if(pid>0)
{
printf("I'm parent process\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);
//解除印射
shmdt(buf);
//消除共享内存
shmctl(shm_id,IPC_RMID,NULL);
//
shmdctl(shm_id1);
return 0;
}
2)读写锁的属性
3)条件变量的属性
4,线程的私有数据
5,线程和fork,如何在线程中安全的使用fork?
线程和fork
例子1:解铃还需系玲人
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"
#include"signal.h"
#include"sys/shm.h"
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
void *pthread_fun(void *arg)
{
sleep(1);//睡眠认出CPU给主线程
pid_t pid;
pid=fork();//新线程里创建子进程
if(pid==0)
{
//这里子进程里要锁住互斥量不成功,因为互斥量已经被主进程锁了
//而且还未被主进程解锁。子进程是无法解锁的,子进程停留在这里
//将不能往下执行。
pthread_mutex_lock(&mutex);
printf("child\n");
pthread_mutex_unlock(&mutex);
}
if(pid>0)
{ //当主线程睡眠2秒后,解锁完主进程就可以锁了。
pthread_mutex_lock(&mutex);
printf("parent\n");
pthread_mutex_unlock(&mutex);
}
}
int main()
{
pthread_t tid;
int ret;
ret=pthread_create(&tid,NULL,pthread_fun,NULL);
if(ret!=0)
{
printf("create new thread failure\n");
return -1;
}
//主线程里锁住互斥量
pthread_mutex_lock(&mutex);
sleep(2);//睡眠2秒,让新线程执行
pthread_mutex_unlock(&mutex);
printf("main\n");
pthread_join(tid,NULL);
return 0;
}
运行结果如图:
由于fork的子进程是主进程的一个副本,副本里有主进程锁住的互斥量,子进程无法解锁,结果子进程里的“child”无法输出打印。
如果在fork()子进程前就解锁,这样子进程就可以lock和unlock了,结果如下。
再者在子线程里fork前加锁,如下:
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"
#include"signal.h"
#include"sys/shm.h"
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
void *pthread_fun(void *arg)
{
sleep(1);//睡眠认出CPU给主线程
pid_t pid;
pthread_mutex_lock(&mutex);
pid=fork();
if(pid==0)
{
pthread_mutex_unlock(&mutex);
printf("child\n");
}
if(pid>0)
{
pthread_mutex_unlock(&mutex);
printf("parent\n");
sleep(1);
}
}
int main()
{
pthread_t tid;
int ret;
ret=pthread_create(&tid,NULL,pthread_fun,NULL);
if(ret!=0)
{
printf("create new thread failure\n");
return -1;
}
printf("main\n");
pthread_join(tid,NULL);
return 0;
}
这样子线程在fork的时候就会把pthread_mutex_lock(&mutext)也fork进去了;
结果子进程就可以解锁了,结果如下:
例子:安全使用fork,调用atfork函数进行解锁;
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"pthread.h"
#include"stdlib.h"
#include"string.h"
#include"limits.h"
#include"signal.h"
#include"sys/shm.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");
sleep(1);
}
void child()
{
pthread_mutex_unlock(&mutex);
printf("I'm child\n");
}
void *pthread_fun(void *arg)
{
sleep(1);//睡眠认出CPU给主线程
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;
int ret;
ret=pthread_create(&tid,NULL,pthread_fun,NULL);
if(ret!=0)
{
printf("create new thread failure\n");
return -1;
}
pthread_mutex_lock(&mutex);
sleep(2);
pthread_mutex_unlock(&mutex);
printf("main\n");
pthread_join(tid,NULL);
return 0;
}
运行结果如下:
运行fork前调用prepare函数进行加锁,fork返回主进程前调用parent解锁,在fork返回子进程前调用child解锁。
最后,多线程总结