最近学习多线程编程,感觉代码的逻辑运行机理(也即是多个线程的切换、调用机理)是多线程编程需要注意的。
先看下面的例程:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t mutex; /* 定义互斥量 */
int x; /* 定义全局变量 */
void thread1(void) /* 定义线程1运行的函数,其功能是对全局变量x进行逐减操作 */
{
while(x>0)
{
pthread_mutex_lock(&mutex); /* 对互斥量进行加锁操作 */
printf("Thread 1 is running : x=%d \n",x);
x--;
pthread_mutex_unlock(&mutex); /* 对互斥量进行解锁操作 */
sleep(1);
}
pthread_exit(NULL);
}
void thread2(void) /* 定义线程2运行的函数,功能与thread2相同 */
{
while(x>0)
{
pthread_mutex_lock(&mutex); /* 对互斥量进行加锁操作 */
printf("Thread 2 is running : x=%d \n",x);
x--;
pthread_mutex_unlock(&mutex); /* 对互斥量进行解锁操作 */
sleep(1);
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t id1,id2; /* 定义线程的标识符 */
int ret;
ret = pthread_mutex_init(&mutex,NULL); /* 对互斥量进行初始化,这里使用默认的属性 */
if(ret != 0)
{
printf ("Mutex initialization failed.\n"); /* 如果初始化失败,打印错误信息 */
exit (1);
}
x=10; /* 对全局变量赋初值 */
ret = pthread_create(&id1, NULL, (void *)&thread1, NULL);/* 创建线程1 */
if(ret != 0)
{
printf ("Thread1 creation failed.\n");
exit (1);
}
ret = pthread_create(&id2, NULL, (void *)&thread2, NULL);/* 创建线程2 */
if(ret != 0)
{
printf ("Thread2 creation failed.\n");
exit (1);
}
pthread_join(id1, NULL); /*线程合并 */
pthread_join(id2, NULL);
return (0);
}
上述例程只是运用简单的互斥量,实现了两个不同进程间的切换运行。
再看下面的例程:
#include <stdio.h>#include <stdlib.h>#include <pthread.h>pthread_mutex_t mutex; /* 定义互斥量 */pthread_cond_t cond; /* 定义条件变量 */int x; /* 定义全局变量 */void producer(void) /* 定义生产者线程运行的函数,其功能是对全局变量x进行逐加操作 */{ while(1) { pthread_mutex_lock(&mutex); /* 对互斥量进行加锁操作 */ int i; for(i=0;i<3-x;i++) /* 当x的值小于3时,进行生产,即对x进行递加操作 */ { x++; printf("Producing : x=%d \n",x); sleep(1); } if(x>=3) /* 当x的值等于3时,通知消费者 */ { pthread_cond_signal(&cond); /* 激活等待的消费者线程 */ printf("Producing completed.\n",x); } pthread_mutex_unlock(&mutex); /* 对互斥量进行解锁操作 */ sleep(1); } pthread_exit(NULL); /* 终止当前线程 */}void consumer(void) /* 定义消费者线程运行的函数,其功能是对全局变量x进行逐减操作 */{ while(1) { pthread_mutex_lock(&mutex); /* 对互斥量进行加锁操作 */ while(x<3) { pthread_cond_wait(&cond,&mutex); /*阻塞消费者线程*/ printf("Start consuming.\n",x); } while(x>0) /* 当x的值大于0时,进行消费,即对x进行递减操作 */ { x--; printf("Consuming : x=%d \n",x); sleep(1); } pthread_mutex_unlock(&mutex); /* 对互斥量进行解锁操作 */ } pthread_exit(NULL); /* 终止当前线程 */} 53int main(void){ pthread_t id1,id2; /* 定义线程的标识符 */ int ret; ret = pthread_mutex_init(&mutex, NULL); /* 对互斥量进行初始化 */ if(ret != 0) { printf ("Mutex initialization failed.\n"); exit (1); } ret = pthread_cond_init(&cond, NULL); /* 对条件变量进行初始化 */ if(ret != 0) { printf ("Conditions initialization failed.\n"); exit (1); } ret = pthread_create(&id1, NULL, (void *)&producer, NULL);/* 创建生产者线程 */ if(ret != 0) { printf ("Thread Producer creation failed.\n"); exit (1); } ret = pthread_create(&id2, NULL, (void *)&consumer, NULL);/* 创建消费者线程 */ if(ret != 0) { printf ("Thread Consumer creation failed.\n"); exit (1); } pthread_join(id1,NULL); /*线程合并 */ pthread_join(id2,NULL); return (0);}
这个例程调用了“条件变量”,实现在某个条件下,对进程的阻塞和激活,编译运行后:
Product :x=1
Product :x=2
Product :x=3
Producing completed.
Start consuming.
Consuming:x=2
Consuming:x=1
Consuming:x=0
Product :x=1
Product :x=2
Product :x=3
Producing completed.
Start consuming.
Consuming:x=2
Consuming:x=1
Consuming:x=0
。。。。。。。
其关键循环条件的形成是在这句:
for(i=0;i<3-x;i++) /* 当x的值小于3时,进行生产,即对x进行递加操作 */
{
x++;
printf("Producing : x=%d \n",x);
sleep(1);
}
如果仔细对这个循环分析,你会发现这句话使x=2,即跳出。也就是说如果没有“条件变量”的话,将会输出:
x=1
x=2
x=1
x=2
即递增到2减到1 然后再递增到2。。。。。
那么有了条件变量后呢,程序是如何运行的呢?
线程1运行:
当x=2后,互斥量解锁,然后切换到线程2;
线程2运行:
判断x<3,然后阻塞此线程,切换到线程1;
线程1运行:
此时i重新赋值i=0,然后x++,当x=3时,线程1激活线程2,待线程1解锁后,切换到线程2;
线程2运行:
x- -,直到x=0,线程2解锁,切换到线程1;
。。。。。。
在实际运行中,也能明显感受到,X=2后,会停留比之前长的时间,然后才输出:X=3。这就是线程切换引起的延时。如果不仔细分析,我们会认为一个循环,线程只是切换了一次,其实不是这样的!