线程同步
线程的主要优点是通过全局变量来共享信息,即会带了另一个问题,即同一变量可能被多个线程同时访问修改,为避免此问题,使用互斥量来确保同一时刻只有一个线程来访问资源。
互斥量既可以静态分配,也可动态分配。需要注意的是:在使用中,如果其他线程(或自己本身线程)已经锁定的这一互斥量,调用mutex_lock()会一直阻塞(或发生死锁)。
同样使用pthread_mutex_unlock()去解锁未锁定的互斥量,或其他线程的互斥量,一样会发生错误。
以下示例代码:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
//初始化互斥量
static int g_glob=0;
static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
static void *ThreadFun(void *argv)
{
int i=*((int *)argv);
int erro,cnt=0,j=0;
for(j=0;j<i;j++)
{
//上锁
erro=pthread_mutex_lock(&mutex);
if(erro!=0)
printf("pthread_mutex_lock fail\n");
cnt=g_glob;
cnt++;
g_glob=cnt;
//解锁
erro=pthread_mutex_unlock(&mutex);
if(erro!=0)
printf("pthread_mutex_unlock fail\n");
}
}
int main(int argc,char **argv)
{
pthread_t thread1,thread2;
int num=atoi(argv[1]),erro;
erro= pthread_create(&thread1,NULL,ThreadFun,&num);
if(erro!=0)
printf("create thread1 fail\n");
erro= pthread_create(&thread2,NULL,ThreadFun,&num);
if(erro!=0)
printf("create thread2 fail\n");
sleep(5);
printf("g_glob=%d\n",g_glob);
exit(EXIT_SUCCESS);
}
互斥量死锁:
一个线程同时访问多个不同的共享资源,且每个资源由不同的互斥量管理,如果处理不当就会造成死锁。
为避免这种问题采用以下两种方法:
①多线程对同一组互斥量操作时,用相同顺序对改组互斥量进行锁定。
②先使用pthread_mutex_lock()锁定第一个互斥量,再使用pthread_mutex_trylock()尝试锁定。如果失败,就等一段时间再来锁定,反复调用此函数(费时间)。
接下来来说说动态初始化互斥量:
动态初始化互斥量函数:int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
销毁动态分配的互斥量函数:ptrread_mutex_destroy()
互斥量的属性之一类型:
pthread_mutex_init()在arg参数中指定pthread_mutexattr_t 类型对象,那么互斥量的类型有哪些呢?
PTHREAD_MUTEX_NORMAL:该类型的互斥量不具备死锁检测功能。
PTHREAD_MUTEX_ERRORCHECK:具备错误检测机制。
PTHREAD_MUTEX_RECURSIVE:采用递归的办法维持一个锁,当线程第一次取得互斥量,会将锁计数器置1,当同一线程再次加锁时,计数器递增。当每解锁一次时,计数器递减,直到减到0,然后释放互斥量。
互斥量的伙伴:
一、条件变量:
1.分配
静态分配:pthread_cond_t cond=PRHREAD_COND_INITIALIZER;
动态分配:
2.通知和等待条件变量:
发信号函数:int pthread_cond_signal();int pthread_cond_broadcast();前者一般只唤醒一个线程(发一个信号),在多处理器上可能唤醒多个线程如果有多个线程在等待某个条件变量时,则需要根据他们的优先级来决定谁先,当优先级也相同时,则根据等待时间来确定。后者则会唤醒所有线程。
等待信号函数:pthread_conf_wait()
下面为条件变量,线程连接实例:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
static pthread_cond_t threadDied=PTHREAD_COND_INITIALIZER;
static pthread_mutex_t threadMutex=PTHREAD_MUTEX_INITIALIZER;
static int totThreads=0;
static int numLive=0;
static int numUnjoined=0;
enum tstate{
TS_alive,
TS_terminated,//终结
TS_joined
};
static struct {
pthread_t id;
int num;
enum tstate state;
int sleeptime;
}*p_thread;
static void *Func(void *arg)
{
int id=*((int *)arg);
int erro;
sleep(p_thread[id].sleeptime);//simulate doing some work;
printf("thread %d terminated\n",id);
erro=pthread_mutex_lock(&threadMutex);
if(erro!=0)
{
printf("pthread_mutex_lock fail\n");
}
numUnjoined++;
p_thread[id].state=TS_terminated;
erro=pthread_mutex_unlock(&threadMutex);
if(erro!=0)
{
printf("pthread_mutex_unlock fail\n");
}
erro=pthread_cond_signal(&threadDied);
if(erro!=0)
{
printf("pthread_cond_signal fail\n");
}
return NULL;
}
int main(int argc,char **argv)
{
int i=0,erro;
int a=0,b=1,c=2;
if(argc<2||strcmp(argv[1],"--help")==0)
{
return 0;
}
p_thread=calloc(argc-1,sizeof(*p_thread));
//creat all thread
for(i=0;i<argc-1;i++)
{
p_thread[i].sleeptime=atoi(argv[i+1]);
p_thread[i].state=TS_alive;
p_thread[i].num=i;
//创建线程
erro=pthread_create(&p_thread[i].id,NULL,Func,&p_thread[i].num);
if(erro!=0)
{
printf("pthread_create fail\n");
}
}
totThreads=argc-1;
numLive=totThreads;
while(numLive>0)
{
//上锁
erro=pthread_mutex_lock(&threadMutex);
if(erro!=0)
{
printf("pthread_mutex_lock fail\n");
}
while(numUnjoined==0)
{
erro=pthread_cond_wait(&threadDied,&threadMutex);
if(erro!=0)
{
printf("pthread_cond_wait fail\n");
}
}
for(i=0;i<totThreads;i++)
{
if(p_thread[i].state==TS_terminated)
{
erro=pthread_join(p_thread[i].id,NULL);
if(erro!=0)
{
printf("pthread_join fail\n");
}
p_thread[i].state=TS_joined;
numLive--;
numUnjoined--;
printf("reaped thread %d (numLive=%d)\n",i,numLive);
}
}
erro=pthread_mutex_unlock(&threadMutex);
if(erro!=0)
{
printf("pthread_mutex_unlock fail\n");
}
}
exit(EXIT_SUCCESS);
}