一、昨日回顾
1、信号处理sigaction
2、使用sigaction如何实现不断重入效果?
struct sigaction act;
act.sa_flags=SA_SIGINFO|SA_NODEFER;
3、想在2号信号的处理流程过程中,屏蔽3号信号
struct sigaction act;
act.sa_flags=SIGINFO;
act.sa_sigaction=sig_Func;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGQUIT);
4、如何设置信号屏蔽?
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask,SIGQUIT);//设置屏蔽信号
sigprocmask(SIG_BLOCK,&mask,NULL);//增加屏蔽
sleep(3);
sigpromask(SIG_UNBLOCK,&mask,NULL);//解除屏蔽
5、线程pthread
(1)线程创建pthread_create(&pthid,NULL,thread_Func,NULL);
(2)线程退出pthread_exit((void)*2)
(3)线程合并
long threadRet;
pthread_join(pthid,(void**)&threadPRet);
二、上午内容
1、线程清理
(1)查看清理函数的执行顺序
①代码示例pthread_cleanup_exec.c(已完成)
#include"func.h"
void cleanup(void*p)
{
printf("I am cleanup %ld\n",(long)p);
}
void *thread_Func(void *p)
{
pthread_cleanup_push(cleanup,(void*)1);
pthread_cleanup_push(cleanup,(void*)2);
pthread_exit(NULL);
pthread_cleanup_pop(1);
pthread_cleanup_pop(2);
}
int main(int argc, char* argv[])
{
pthread_t pthid;
int ret=pthread_create(&pthid,NULL,thread_Func,NULL);
long threadRet;
ret=pthread_join(pthid,(void **)&threadRet);
THREAD_ERROR_CHECK(ret,"pthread_join");
printf("threadRet=%ld",threadRet);
return 0;
}
Ⅰ先压栈的清理函数后执行
Ⅱ该功能使用的是清理栈
②工作的时候如何写清理资源?
代码示例pthread_cleanup_malloc.c(已完成)
#include"func.h"
void cleanup(void*p)
{
free(p);
printf("I am cleanup\n");
}
void *thread_Func(void *p)
{
p=malloc(20);
pthread_cleanup_push(cleanup,p);
sleep(1);
pthread_cleanup_pop(1);
pthread_exit(NULL);
}
int main(int argc, char* argv[])
{
pthread_t pthid;
int ret=pthread_create(&pthid,NULL,thread_Func,NULL);
long threadRet;
ret=pthread_join(pthid,(void **)&threadRet);
THREAD_ERROR_CHECK(ret,"pthread_join");
printf("threadRet=%ld",threadRet);
return 0;
}
2、线程的同步与互斥:双方互斥进行
(1)互斥锁
①锁的初始化
Ⅰ使用函数
pthread_mutex_t mutex;
pthread_mutex_init;
②加锁操作
Ⅰ使用函数pthread_mutex_lock();
Ⅱ代码示例pthread_add2000.c
#include <func.h>
#define N 20000000
typedef struct{
int val;
pthread_mutex_t mutex;
}Data_t;
void* threadFunc(void* p)
{
int i;
Data_t* threadInfo=(Data_t*)p;
for(i=0;i<N;i++)
{
pthread_mutex_lock(&threadInfo->mutex);
threadInfo->val+=1;
pthread_mutex_unlock(&threadInfo->mutex);
}
}
int main(int argc,char* argv[])
{
pthread_t pthid;
int ret;
Data_t threadInfo;
threadInfo.val=0;
ret=pthread_mutex_init(&threadInfo.mutex,NULL);
THREAD_ERROR_CHECK(ret,"pthread_mutex_init");
struct timeval start,end;
gettimeofday(&start,NULL);
ret=pthread_create(&pthid,NULL,threadFunc,&threadInfo);
THREAD_ERROR_CHECK(ret,"pthread_create");
int i;
for(i=0;i<N;i++)
{
pthread_mutex_lock(&threadInfo.mutex);
threadInfo.val+=1;
pthread_mutex_unlock(&threadInfo.mutex);
}
pthread_join(pthid,NULL);//等待子线程
gettimeofday(&end,NULL);
printf("I am main thread,val=%d,use time=%ld\n",threadInfo.val,(end.tv_sec-start.tv_sec)*1000000+end.tv_usec-start.tv_usec);
return 0;
}
③解锁操作
Ⅰ代码例子pthread_mutex_trylock.c
#include <func.h>
typedef struct{
pthread_mutex_t mutex;
}Data_t;
void* threadFunc(void* p)
{
Data_t* pArg=(Data_t*)p;
int ret=pthread_mutex_trylock(&pArg->mutex);
CHILD_THREAD_ERROR_CHECK(ret,"pthread_mutex_trylock");
printf("child lock success\n");
pthread_exit(NULL);
}
int main(int argc,char* argv[])
{
Data_t threadInfo;
pthread_t pthid;
pthread_mutex_init(&threadInfo.mutex,NULL);
pthread_create(&pthid,NULL,threadFunc,&threadInfo);
pthread_mutex_lock(&threadInfo.mutex);
printf("main thread lock success\n");
int ret=pthread_join(pthid,NULL);
THREAD_ERROR_CHECK(ret,"pthread_join");
return 0;
}
④测试加锁
Ⅰ代码示例sem_add1000.c
#include <func.h>
#define N 20000000
int main(int argc,char* argv[])
{
int semArrId=semget(1000,1,IPC_CREAT|0600);
ERROR_CHECK(semArrId,-1,"semget");
int shmid;
shmid=shmget(1000,1<<20,IPC_CREAT|0600);
ERROR_CHECK(shmid,-1,"shmget");
int *p;
p=(int*)shmat(shmid,NULL,0);
ERROR_CHECK(p,(int*)-1,"shmat");
*p=0;
int ret=semctl(semArrId,0,SETVAL,1);
ERROR_CHECK(ret,-1,"semctl");
struct sembuf sopp,sopv;
sopp.sem_num=0;
sopp.sem_op=-1;
sopp.sem_flg=SEM_UNDO;
sopv.sem_num=0;
sopv.sem_op=1;
sopv.sem_flg=SEM_UNDO;
struct timeval start,end;
gettimeofday(&start,NULL);
if(!fork())
{
int i;
for(i=0;i<N;i++)
{
//加锁
semop(semArrId,&sopp,1);
p[0]=p[0]+1;
semop(semArrId,&sopv,1);
//解锁
}
}else{
int i;
for(i=0;i<N;i++)
{
semop(semArrId,&sopp,1);
p[0]=p[0]+1;
semop(semArrId,&sopv,1);
}
wait(NULL);
gettimeofday(&end,NULL);
printf("result=%d,use time=%ld\n",p[0],(end.tv_sec-start.tv_sec)*1000000+end.tv_usec-start.tv_usec);
}
return 0;
}
(2)别人加的锁,可以解开吗?
①
Ⅰ代码例子pthread_mutex_trylock.c
#include <func.h>
typedef struct{
pthread_mutex_t mutex;
}Data_t;
void* threadFunc(void* p)
{
Data_t* pArg=(Data_t*)p;
int ret=pthread_mutex_trylock(&pArg->mutex);
CHILD_THREAD_ERROR_CHECK(ret,"pthread_mutex_trylock");
printf("child lock success\n");
pthread_exit(NULL);
}
int main(int argc,char* argv[])
{
Data_t threadInfo;
pthread_t pthid;
pthread_mutex_init(&threadInfo.mutex,NULL);
pthread_create(&pthid,NULL,threadFunc,&threadInfo);
pthread_mutex_lock(&threadInfo.mutex);
printf("main thread lock success\n");
int ret=pthread_join(pthid,NULL);
THREAD_ERROR_CHECK(ret,"pthread_join");
return 0;
}
Ⅱ代码例子pthread_mutex_unlock_other.c
#include <func.h>
typedef struct{
pthread_mutex_t mutex;
}Data_t;
void* threadFunc(void* p)
{
Data_t* pArg=(Data_t*)p;
pthread_mutex_unlock(&pArg->mutex);
pthread_mutex_lock(&pArg->mutex);
printf("child lock success\n");
pthread_exit(NULL);
}
int main(int argc,char* argv[])
{
Data_t threadInfo;
pthread_t pthid;
pthread_mutex_init(&threadInfo.mutex,NULL);
pthread_create(&pthid,NULL,threadFunc,&threadInfo);
pthread_mutex_lock(&threadInfo.mutex);
printf("main thread lock success\n");
int ret=pthread_join(pthid,NULL);
THREAD_ERROR_CHECK(ret,"pthread_join");
return 0;
}
锁传递,应该传递地址
3、使用线程锁统计时间
三、下午的内容
1、如何加锁?
①加两次锁
使用命令pthread_mutex_lock
代码例子pthread_mutex_locktwice.c(已完成)
给锁加两次锁
#include"func.h"
int main(int argc,char* argv[])
{
pthread_mutex_t mutex;//定义锁
pthread_mutexattr_t mattr;//定义互斥锁属性
int ret;//定义返回值
ret=pthread_mutexattr_init(&mattr);//初始化互斥锁属性
THREAD_ERROR_CHECK(ret,"pthread_mutexattr_init");//错误检查
pthread_mutexattr_settype(&mattr,PTHREAD_MUTEX_RECURSIVE);//设置互斥锁属性类型
pthread_mutex_init(&mutex,&mattr);//初始化互斥锁
pthread_mutex_lock(&mutex);//第一次加锁
ret=pthread_mutex_lock(&mutex);//第二次加锁,得到返回值
THREAD_ERROR_CHECK(ret,"pthread_mutex_lock");//错误检查
printf("you can see me\n");//打印输出
return 0;
}
②如何检测是否有错误
代码示例thread_mutex_errorcheck.c
避免对同一把锁加锁两次
#include <func.h>
int main(int argc,char* argv[])
{
pthread_mutex_t mutex;
pthread_mutexattr_t mattr;
int ret;
ret=pthread_mutexattr_init(&mattr);
THREAD_ERROR_CHECK(ret,"pthread_mutexattr_init");
pthread_mutexattr_settype(&mattr,PTHREAD_MUTEX_ERRORCHECK);//允许同一个线程对同一把锁加锁多次
pthread_mutex_init(&mutex,&mattr);
pthread_mutex_lock(&mutex);
ret=pthread_mutex_lock(&mutex);
THREAD_ERROR_CHECK(ret,"pthread_mutex_lock");
printf("you can see me\n");
return 0;
}
③使用进程锁进行2000万加法。
代码示例fork_mutex.c
#include <func.h>
#define N 20000000
typedef struct{
int val;
pthread_mutex_t mutex;
}Data_t,*pData_t;
int main(int argc,char* argv[])
{
int semArrId=semget(1000,1,IPC_CREAT|0600);
ERROR_CHECK(semArrId,-1,"semget");
int shmid;
shmid=shmget(1000,1<<20,IPC_CREAT|0600);
ERROR_CHECK(shmid,-1,"shmget");
pData_t p;
p=(pData_t)shmat(shmid,NULL,0);
ERROR_CHECK(p,(pData_t)-1,"shmat");
p->val=0;
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mattr,PTHREAD_PROCESS_SHARED);//设置为进程共享
int ret=pthread_mutex_init(&p->mutex,&mattr);
THREAD_ERROR_CHECK(ret,"pthread_mutex_init");
struct timeval start,end;
gettimeofday(&start,NULL);
if(!fork())
{
int i;
for(i=0;i<N;i++)
{
//加锁
pthread_mutex_lock(&p->mutex);
p->val+=1;
pthread_mutex_unlock(&p->mutex);
//解锁
}
}else{
int i;
for(i=0;i<N;i++)
{
pthread_mutex_lock(&p->mutex);
p->val+=1;
pthread_mutex_unlock(&p->mutex);
}
wait(NULL);
gettimeofday(&end,NULL);
printf("result=%d,use time=%ld\n",p->val,(end.tv_sec-start.tv_sec)*1000000+end.tv_usec-start.tv_usec);
}
return 0;
}
tips:
Ⅰ锁使用的区域是在共享内存内
Ⅱ在加锁代码中,先加锁,在执行操作,之后再解锁
④代码示例sale_tickets.c
#include <func.h>
typedef struct{
int tickets;
pthread_mutex_t mutex;
}Data_t;
void cleanup(void *p)
{
pthread_mutex_unlock((pthread_mutex_t*)p);
printf("unlock success\n");
}
void* saleWindows1(void* p)
{
Data_t* pArg=(Data_t*)p;
int i=0;
while(1)
{
pthread_mutex_lock(&pArg->mutex);
if(pArg->tickets>0)
{
//printf("I am saleWindows1 start sale,%d\n",pArg->tickets);
pArg->tickets--;
i++;
//printf("I am saleWindows1 sale finish,%d\n",pArg->tickets);
pthread_mutex_unlock(&pArg->mutex);
}else{
pthread_mutex_unlock(&pArg->mutex);
printf("I am saleWindows1,%d\n",i);
break;
}
}
return NULL;
}
void* saleWindows2(void* p)
{
Data_t* pArg=(Data_t*)p;
int i=0;
while(1)
{
pthread_mutex_lock(&pArg->mutex);
if(pArg->tickets>0)
{
//printf("I am saleWindows2 start sale,%d\n",pArg->tickets);
pArg->tickets--;
i++;
//printf("I am saleWindows2 sale finish,%d\n",pArg->tickets);
pthread_mutex_unlock(&pArg->mutex);
}else{
pthread_mutex_unlock(&pArg->mutex);
printf("I am saleWindows2,%d\n",i);
break;
}
}
return NULL;
}
int main(int argc,char* argv[])
{
Data_t threadInfo;
threadInfo.tickets=20000000;
pthread_t pthid,pthid1;
pthread_mutex_init(&threadInfo.mutex,NULL);
pthread_create(&pthid,NULL,saleWindows1,&threadInfo);
pthread_create(&pthid1,NULL,saleWindows2,&threadInfo);
int ret;
long threadRet;
ret=pthread_join(pthid1,(void**)&threadRet);
THREAD_ERROR_CHECK(ret,"pthread_join");
ret=pthread_join(pthid,(void**)&threadRet);
THREAD_ERROR_CHECK(ret,"pthread_join");
printf("sale over\n");
return 0;
}
tips:
Ⅰ在判断有没有票之前,就要加一个锁
2、条件变量
(1)两个线程要设置等待和激发
Ⅰ等待条件有两种方式:无条件等待和有条件等待
Ⅱ通过这种方式,就可以实现线程同步
!!要注意区别:进程间通信和进程间的同步
进程间的同步:可以通过管道、消息队列、或者信号量实现
①等待与激发
Ⅰ首先要初始化一个参数
代码示例:(子线程等待,主线程执行)
Pthread_cond.c
#include <func.h>
typedef struct{
pthread_mutex_t mutex;
pthread_cond_t cond;
}Data_t,*pData_t;
void* threadFunc(void* p)
{
pData_t pArg=(pData_t)p;
pthread_mutex_lock(&pArg->mutex);
pthread_cond_wait(&pArg->cond,&pArg->mutex);
pthread_mutex_unlock(&pArg->mutex);
printf("I am child thread wake up\n");
pthread_exit(NULL);
}
int main(int argc,char* argv[])
{
Data_t threadInfo;
pthread_mutex_init(&threadInfo.mutex,NULL);
pthread_cond_init(&threadInfo.cond,NULL);
pthread_t pthid;
pthread_create(&pthid,NULL,threadFunc,&threadInfo);
usleep(5000);
pthread_cond_signal(&threadInfo.cond);//让条件变量成立
pthread_join(pthid,NULL);
printf("I am main thread\n");
return 0;
}
②一个函数
上半部:
Ⅰ排队在对应的cond
Ⅱ解锁
Ⅲ睡觉
下半部
Ⅰ醒来
Ⅱ加锁
Ⅲ返回
③
Ⅰ条件变量初始化:pthread_cond_init
Ⅱ让条件变量成立pthread_cond_signal//让条件变量成立
tips:内核真正在调度的时候,调度单位是内核线程
④新的代码示例pthread_cond_twowait.c
#include <func.h>
typedef struct{
pthread_mutex_t mutex;
pthread_cond_t cond;
}Data_t,*pData_t;
void cleanup(void *p)
{
pthread_mutex_unlock((pthread_mutex_t*)p);
printf("unlock ok\n");
}
void* threadFunc(void* p)
{
pData_t pArg=(pData_t)p;
pthread_mutex_lock(&pArg->mutex);
pthread_cleanup_push(cleanup,&pArg->mutex);
pthread_cond_wait(&pArg->cond,&pArg->mutex);
printf("wait return\n");
pthread_cleanup_pop(1);
printf("I am child thread wake up\n");
pthread_exit(NULL);
}
#define N 2
int main(int argc,char* argv[])
{
Data_t threadInfo;
pthread_mutex_init(&threadInfo.mutex,NULL);
pthread_cond_init(&threadInfo.cond,NULL);
pthread_t pthid[N];
int i;
for(i=0;i<N;i++)
{
pthread_create(pthid+i,NULL,threadFunc,&threadInfo);
}
int ret;
for(i=0;i<N;i++)
{
ret=pthread_cancel(pthid[i]);
THREAD_ERROR_CHECK(ret,"pthread_cancel");
}
for(i=0;i<N;i++)
{
pthread_join(pthid[i],NULL);
}
printf("I am main thread\n");
return 0;
}
tips:
Ⅰ多个线程在等待,代码示例是2个线程在等待
Ⅱ如何解决无法cancel线程的问题?使用cleanup,其中push放在wait前边,pop放在wait后边;要注意,要定义一个cleanup函数
⑤超时等待,代码示例
Pthread_cond_timedwait.c
#include <func.h>
typedef struct{
pthread_mutex_t mutex;
pthread_cond_t cond;
}Data_t,*pData_t;
void* threadFunc(void* p)
{
pData_t pArg=(pData_t)p;
struct timespec t;
t.tv_sec=time(NULL)+3;
t.tv_nsec=0;
int ret;
pthread_mutex_lock(&pArg->mutex);
ret=pthread_cond_timedwait(&pArg->cond,&pArg->mutex,&t);
pthread_mutex_unlock(&pArg->mutex);
printf("I am child thread wake up,ret=%d\n",ret);
pthread_exit(NULL);
}
int main(int argc,char* argv[])
{
Data_t threadInfo;
pthread_mutex_init(&threadInfo.mutex,NULL);
pthread_cond_init(&threadInfo.cond,NULL);
pthread_t pthid;
pthread_create(&pthid,NULL,threadFunc,&threadInfo);
sleep(1);
pthread_cond_signal(&threadInfo.cond);
pthread_join(pthid,NULL);
printf("I am main thread\n");
return 0;
}