好了,这篇讲解多线程的同步问题。
互斥是同步的一种,linux下提供了多种方式来处理线程同步,最常用的是互斥量,条件变量和信号量。
本文首先使用互斥锁,信号量来实现同步版本,然后在深层次的分析下互斥锁和信号量的实现机制和原理。
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include <unistd.h>
#include <semaphore.h>
pthread_mutex_t mutex;
pthread_mutex_t mutex2;
sem_t sem;
long int g_nCount = 0;
void* mythread1(void*pPM)
{
// usleep(10);
long int nThreadNum = *(long *)pPM;
sem_post(&sem);
usleep(100);
pthread_mutex_lock(&mutex2);
g_nCount++;
printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nCount);
pthread_mutex_unlock(&mutex2);
return 0;
}
void* mythread(void*pPM)
{
long int nThreadNum = (long )pPM;
pthread_mutex_unlock(&mutex);
usleep(100);
pthread_mutex_lock(&mutex2);
g_nCount++;
printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nCount);
pthread_mutex_unlock(&mutex2);
return 0;
}
int main()
{
printf(" 子线程报数\n");
const int THREAD_NUM =10;
pthread_t id[THREAD_NUM] ;
pthread_mutex_init(&mutex, NULL);
sem_init (&sem, 0, 1);
pthread_mutex_init(&mutex2, NULL);
for (int i = 0; i < THREAD_NUM; )
{
pthread_mutex_lock(&mutex);
i++;
pthread_create(&id[i-1], NULL, mythread, (void*)i);
}
for (int i = 0; i < THREAD_NUM; i++)
pthread_join(id[i], NULL);
printf("有%d个用户登录后记录结果是%d\n", THREAD_NUM, g_nCount);
g_nCount = 0;
for (int i = 0; i < THREAD_NUM; )
{
sem_wait (&sem);
i++;
pthread_create(&id[i-1], NULL, mythread1, (void*)&i);
}
for (int i = 0; i < THREAD_NUM; i++)
pthread_join(id[i], NULL);
printf("有%d个用户登录后记录结果是%d\n", THREAD_NUM, g_nCount);
sem_destroy(&sem);
pthread_mutex_destroy(&mutex);
return 0;
}
下面是实验结果
这里 子线程报数
线程编号为3 全局资源值为1
线程编号为4 全局资源值为2
线程编号为1 全局资源值为3
线程编号为2 全局资源值为4
线程编号为5 全局资源值为5
线程编号为6 全局资源值为6
线程编号为7 全局资源值为7
线程编号为8 全局资源值为8
线程编号为9 全局资源值为9
线程编号为10 全局资源值为10
有10个用户登录后记录结果是10
线程编号为2 全局资源值为1
线程编号为1 全局资源值为2
线程编号为4 全局资源值为3
线程编号为3 全局资源值为4
线程编号为5 全局资源值为5
线程编号为6 全局资源值为6
线程编号为7 全局资源值为7
线程编号为8 全局资源值为8
线程编号为9 全局资源值为9
线程编号为10 全局资源值为10
有10个用户登录后记录结果是10
写代码片
可以看出来,各子线程已经可以互斥的访问与输出全局资源了,但主线程与子线程之间的同步还是有点问题。
至于问题等我有空的时候好好分析一番。
下面要讲的是条件变量了。
所需头文件 | include<pthread.h> |
函数原型 | int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr); int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex); int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime); int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); |
函数参数 | cond:条件变量 mutex:互斥量 |
函数返回值 |
下面用条件变量来实现同步的例子。
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include <unistd.h>
#include <semaphore.h>
pthread_mutex_t mutex;
pthread_mutex_t mutex2;
pthread_cond_t cond;
long int g_nCount = 0;
void* mythread(void*pPM)
{
pthread_mutex_lock(&mutex);
long int nThreadNum = (long )pPM;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
usleep(100);
pthread_mutex_lock(&mutex2);
g_nCount++;
printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nCount);
pthread_mutex_unlock(&mutex2);
return 0;
}
int main()
{
printf(" 子线程报数\n");
const int THREAD_NUM =10;
pthread_t id[THREAD_NUM] ;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex2, NULL);
for (int i = 0; i < THREAD_NUM; )
{
pthread_mutex_lock(&mutex);
i++;
pthread_create(&id[i-1], NULL, mythread, (void*)i);
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
}
for (int i = 0; i < THREAD_NUM; i++)
pthread_join(id[i], NULL);
printf("有%d个用户登录后记录结果是%d\n", THREAD_NUM, g_nCount);
pthread_mutex_destroy(&mutex);
pthread_mutex_destroy(&mutex2);
pthread_cond_destroy(&cond);
return 0;
}
实验结果如下:
子线程报数
线程编号为1 全局资源值为1
线程编号为2 全局资源值为2
线程编号为7 全局资源值为3
线程编号为3 全局资源值为4
线程编号为4 全局资源值为5
线程编号为5 全局资源值为6
线程编号为6 全局资源值为7
线程编号为8 全局资源值为8
线程编号为9 全局资源值为9
线程编号为10 全局资源值为10
有10个用户登录后记录结果是10
条件变量为什么要和互斥锁结合一起?
互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起配合使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其他的某个线程改变了条件变量,他将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。
两个线程操作同一临界区时,通过互斥锁保护,若A线程已经加锁,B线程再加锁时候会被阻塞,直到A释放锁,B再获得锁运行,进程B必须不停的主动获得锁、检查条件、释放锁、再获得锁、再检查、再释放,一直到满足运行的条件的时候才可以(而此过程中其他线程一直在等待该线程的结束),这种方式是比较消耗系统的资源的。而条件变量同样是阻塞,还需要通知才能唤醒,线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,该线程就休眠了,应该仍阻塞在这里,等待条件满足后被唤醒,节省了线程不断运行浪费的资源。这个过程一般用while语句实现。当线程B发现被锁定的变量不满足条件时会自动的释放锁并把自身置于等待状态,让出CPU的控制权给其它线程。其它线程 此时就有机会去进行操作,当修改完成后再通知那些由于条件不满足而陷入等待状态的线程。这是一种通知模型的同步方式,大大的节省了CPU的计算资源,减少了线程之间的竞争,而且提高了线程之间的系统工作的效率。这种同步方式就是条件变量。
参考文献:
[1].http://blog.csdn.net/morewindows/article/details/7470936
[2].http://blog.csdn.net/sishuiliunian0710/article/details/9625407
[3].UNIX网络编程