线程间同步一 ———— 互斥锁(mutex)
应用实例
/*
当主线程输入com_buf[]不为空时,子线程计算该com_buf的长度。
使用互斥锁来同步临界区,确保变量com_buf只被一个线程使用
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#define __DEBUG
/* Enables or disables debug output */
#ifdef __DEBUG
#define DBG(fmt, args...) fprintf(stdout, "XJTU_DBG: " fmt, ## args)
#else
#define DBG(fmt, args...)
#endif
#define ERR(fmt, args...) fprintf(stderr, "XJTU_ERR: " fmt, ## args)
pthread_mutex_t mutex;
/* 主线程输入,子线程计算长度 */
char com_buf[255];
/* 全局变量 */
int time_to_exit = 0;
/* 子线程-统计输入字串com_buf长度 */
void *thread_fun()
{
sleep(1);
/* 临界区同步 */
pthread_mutex_lock(&mutex);
while(strncmp(com_buf,"quit",4)!=0)
{
DBG("You input %d characters.\n",strlen(com_buf)-1);
com_buf[0]='\0';
pthread_mutex_unlock(&mutex);
sleep(1);
/* 周期性检查com_buf,为空不能执行统计 */
pthread_mutex_lock(&mutex);
while(com_buf[0] == '\0')
{
pthread_mutex_unlock(&mutex);
sleep(1);
pthread_mutex_lock(&mutex);
}
}
/* 程序退出标志设置 */
time_to_exit = 1;
com_buf[0] = '\0';
pthread_mutex_unlock(&mutex);
pthread_exit((void *)100);
}
/* 主线程 */
int main(int argc, char *argv[])
{
int err;
void *pthread_exit_result;
pthread_t td;
/* 互斥锁初始化 */
err = pthread_mutex_init(&mutex,NULL);
if(err < 0 )
{
ERR("pthread mutex init error!\n");
exit(-1);
}
/* 子线程创建 */
err = pthread_create(&td,NULL,thread_fun,NULL);
if(err < 0 )
{
ERR("pthread create error!\n");
exit(-1);
}
/* 临界区同步 */
pthread_mutex_lock(&mutex);
DBG("Input some text. Enter 'quit' to finish\n");
while(!time_to_exit)
{
fgets(com_buf,255,stdin);
pthread_mutex_unlock(&mutex);
while(1)
{
pthread_mutex_lock(&mutex);
if(com_buf[0] != '\0')
{
pthread_mutex_unlock(&mutex);
sleep(1);
}
else
break;
}
}
pthread_mutex_unlock(&mutex);
DBG("Waitting for main thread to finish....\n");
err = pthread_join(td,&pthread_exit_result);
if(err<0)
{
ERR("pthread join error!\n");
exit(-1);
}
DBG("sub-pthread exit code %d.\n",(int)pthread_exit_result);
pthread_mutex_destroy(&mutex);
return 0;
}
代码测试
基本界定
1.线程竞争资源最根本的源头在于临界区是个窗口,为拒绝需要在窗口内执行同步访问操作。即互斥访问。
2.原子操作:计算机科学认为一组不可分割的操作。就是说一段代码要么一气执行完不能打断,要么就一句别执行。
3.锁机制可以保证临界区的互斥,解决线程间资源访问的唯一性,通过加锁保证临界区操作的原子性,这种锁机制被叫做互斥锁。
4.临界区:就是需要同步的代码段,或者说是竞争发生的窗口区域。
代码说明
1.代码比较简单,都有注释,不明白的可以留言或mail讨论。
2.通过测试 pthread_exit((void )100);函数返回的是一个(void )指针类型,而pthread_join(td,&pthread_exit_result);中第二个参数接受的返回码是(void **)类型,所有我声明一个 void *pthread_exit_result;类型变量,这样返回的指针100就直接赋值给了指针变量pthread_exit_result,所以如大家所见打印了100.
引入的问题 —— 死锁
这是互斥引入的bug。在互斥的情况下,两个线程都在等待对方持有的互斥锁释放,而导致两个线程都不能往下执行。典型的ABBA死锁(死锁拥抱):线程1获得互斥锁A,线程2获得互斥锁B,当他们要获得另一方的互斥锁时,发现被另一个线程拥有,于是俩线程都阻塞在那里,导致死锁。一种解决方法是使用pthread_mutex_trylock,但这个不是根本解决办法,根本解决办法还是如一位大师所言,最开始的数据顶层设计和线程间逻辑关系就要清新明朗,才能避免死锁。