本文目录一览:
- 1、在linux下用c语言实现用多进程同步方法演示“生产者-消费者”问题
- 2、如何使用pthread
- 3、使用条件变量进行线程间的同步
- 4、Linux C语言pthread_mutex_unlock和pthread_cond_signal顺序问题
- 5、C语言如何终止线程?
- 6、C语言多线程的操作步骤
在linux下用c语言实现用多进程同步方法演示“生产者-消费者”问题
这个问题需要的知识主要包括:
1 多进程间进行通信;
2 使用同步信号量(semaphore)和互斥信号量(mutex)进行数据保护。
参考代码如下,可以参照注释辅助理解:
#include stdio.h
#include stdlib.h
#include unistd.h
#include pthread.h
#include semaphore.h
#define N 2 // 消费者或者生产者的数目
#define M 10 // 缓冲数目
int in = 0; // 生产者放置产品的位置
int out = 0; // 消费者取产品的位置
int buff[M] = {0}; // 缓冲初始化为0, 开始时没有产品
sem_t empty_sem; // 同步信号量, 当满了时阻止生产者放产品
sem_t full_sem; // 同步信号量, 当没产品时阻止消费者消费
pthread_mutex_t mutex; // 互斥信号量, 一次只有一个线程访问缓冲
int product_id = 0; //生产者id
int prochase_id = 0; //消费者id
/* 打印缓冲情况 */
void print()
{
int i;
for(i = 0; i M; i++)
printf("%d ", buff[i]);
printf("\n");
}
/* 生产者方法 */
void *product()
{
int id = ++product_id;
while(1)
{
// 用sleep的数量可以调节生产和消费的速度,便于观察
sleep(1);
//sleep(1);
sem_wait(empty_sem);
pthread_mutex_lock(mutex);
in = in % M;
printf("product%d in %d. like: \t", id, in);
buff[in] = 1;
print();
++in;
pthread_mutex_unlock(mutex);
sem_post(full_sem);
}
}
/* 消费者方法 */
void *prochase()
{
int id = ++prochase_id;
while(1)
{
// 用sleep的数量可以调节生产和消费的速度,便于观察
sleep(1);
//sleep(1);
sem_wait(full_sem);
pthread_mutex_lock(mutex);
out = out % M;
printf("prochase%d in %d. like: \t", id, out);
buff[out] = 0;
print();
++out;
pthread_mutex_unlock(mutex);
sem_post(empty_sem);
}
}
int main()
{
pthread_t id1[N];
pthread_t id2[N];
int i;
int ret[N];
// 初始化同步信号量
int ini1 = sem_init(empty_sem, 0, M);
int ini2 = sem_init(full_sem, 0, 0);
if(ini1 ini2 != 0)
{
printf("sem init failed \n");
exit(1);
}
//初始化互斥信号量
int ini3 = pthread_mutex_init(mutex, NULL);
if(ini3 != 0)
{
printf("mutex init failed \n");
exit(1);
}
// 创建N个生产者线程
for(i = 0; i N; i++)
{
ret[i] = pthread_create(id1[i], NULL, product, (void *)(i));
if(ret[i] != 0)
{
printf("product%d creation failed \n", i);
exit(1);
}
}
//创建N个消费者线程
for(i = 0; i N; i++)
{
ret[i] = pthread_create(id2[i], NULL, prochase, NULL);
if(ret[i] != 0)
{
printf("prochase%d creation failed \n", i);
exit(1);
}
}
//销毁线程
for(i = 0; i N; i++)
{
pthread_join(id1[i],NULL);
pthread_join(id2[i],NULL);
}
exit(0);
}
在Linux下编译的时候,要在编译命令中加入选项-lpthread以包含多线程支持。比如存储的C文件为demo.c,要生成的可执行文件为demo。可以使用命令:
gcc demo.c -o demo -lpthread
程序中为便于观察,使用了sleep(1);来暂停运行,所以查看输出的时候可以看到,输出是每秒打印一次的。
如何使用pthread
怎样知道一个pthread
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
1.从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
2.一个线程可以调用pthread_cancel终止同一进程中的另一个线程。
3.线程可以调用pthread_exit终止自己。
使用条件变量进行线程间的同步
先看一下APUE第三版对于条件变量的说明:
条件变量是另一种线程同步机制,它为线程间共享的对象提供了同步的方法。当条件变量配合互斥锁(Mutex)使用时,允许多个线程处在一种自由等待任意条件发生的状态。
条件变量自身由互斥锁(Mutex)保护。线程必须在修改条件状态之前先对其上锁,其他线程不会在获取锁之前被通知到其状态变化,因为只有获取到锁才可以计算条件。
条件变量的数据类型是 pthread_cond_t ,条件变量属性的类型是 pthread_condattr_t ,它们都包含在头文件pthread.h中。
条件变量使用之前必须初始化,有两种方法:
需要释放条件变量时,使用pthread_cond_destroy即可。
调用phread_cond_wait或pthread_cond_timewait(以下简称wait函数)可以使当前线程等待某一条件的发生。两者的区别在于后者可以指定等待时间。
调用wait函数时,系统使调用线程进入等待状态后 释放锁 (所以我们必须先加锁后调用wait函数)。在这一步操作中的检查条件和进入等待是 原子操作 ,所以线程不会错过条件的变化。当wait函数返回时,mutex会 再次被加锁 。
其中pthread_cond_timewait中用到的timespec结构定义如下:
需要注意的是,timespec是一个绝对时间,所以在使用前我们需要先取得当前时间,再加上等待时间。例如下面这样:
如果时间到了还没有等到条件变化,函数会对mutex重新加锁并返回一个ETIMEOUT的错误。
当wait函数返回成功时, 需要重新检查条件 ,因为条件有可能已经被其他线程修改。
当条件满足时,可以用这两个函数用来通知其他线程。
pthread_cond_signal会唤醒至少一个等待的线程,而pthread_cond_broadcast会唤醒所有等待的线程。必须注意的是:我们 必须在状态发生变化之后再发送信号给其他线程 。
条件变量的数据类型是 pthread_cond_t ,它主要有两种属性:
设置进程间共享属性:
设置时钟属性:
pthread_cond_timewait函数用于在等待条件变量时提供超时功能,不过该函数的超时时间是一个绝对时间。默认使用系统时间,这意味着若修改系统时间,那么超时就不准确:有可能提前返回,也可能要几年才返回。这在某些情况下会导致bug,这时我们可以通过设置条件变量的时钟属性来避免这个问题。下面的例子展示了如何使用这个属性:
Linux C语言pthread_mutex_unlock和pthread_cond_signal顺序问题
signal前解锁是错误,顺序相当重要,锁才能保证你整个操作是完全原子,signal只是整个操作的一部分,它不能被分割出去。用特殊一点的情况来解释:如果解锁后才signal,那么有可能信号一直发不出去,因为信号线程一直得到不调度。
C语言如何终止线程?
面只有两个线程,是生产者/消费者模式,已编译通过,注释很详细。
/* 以生产者和消费者模型问题来阐述Linux线程的控制和通信你
生产者线程将生产的产品送入缓冲区,消费者线程则从中取出产品。
缓冲区有N个,是一个环形的缓冲池。
*/
#include stdio.h
#include pthread.h
#define BUFFER_SIZE 16
struct prodcons
{
int buffer[BUFFER_SIZE];/*实际存放数据的数组*/
pthread_mutex_t lock;/*互斥体lock,用于对缓冲区的互斥操作*/
int readpos,writepos; /*读写指针*/
pthread_cond_t notempty;/*缓冲区非空的条件变量*/
pthread_cond_t notfull;/*缓冲区未满 的条件变量*/
};
/*初始化缓冲区*/
void pthread_init( struct prodcons *p)
{
pthread_mutex_init(p-lock,NULL);
pthread_cond_init(p-notempty,NULL);
pthread_cond_init(p-notfull,NULL);
p-readpos = 0;
p-writepos = 0;
}
/*将产品放入缓冲区,这里是存入一个整数*/
void put(struct prodcons *p,int data)
{
pthread_mutex_lock(p-lock);
/*等待缓冲区未满*/
if((p-writepos +1)%BUFFER_SIZE ==p-readpos)
{
pthread_cond_wait(p-notfull,p-lock);
}
p-buffer[p-writepos] =data;
p-writepos++;
if(p-writepos = BUFFER_SIZE)
p-writepos = 0;
pthread_cond_signal(p-notempty);
pthread_mutex_unlock(p-lock);
}
/*从缓冲区取出整数*/
int get(struct prodcons *p)
{
int data;
pthread_mutex_lock(p-lock);
/*等待缓冲区非空*/
if(p-writepos == p-readpos)
{
pthread_cond_wait(p-notempty ,p-lock);//非空就设置条件变量notempty
}
/*读书据,移动读指针*/
data = p-buffer[p-readpos];
p-readpos++;
if(p-readpos == BUFFER_SIZE)
p-readpos = 0;
/*设置缓冲区未满的条件变量*/
pthread_cond_signal(p-notfull);
pthread_mutex_unlock(p-lock);
return data;
}
/*测试:生产站线程将1 到1000的整数送入缓冲区,消费者线程从缓冲区中获取整数,两者都打印信息*/
#define OVER (-1)
struct prodcons buffer;
void *producer(void *data)
{
int n;
for( n=0;n1000;n++)
{
printf("%d ------\n",n);
put(buffer,n);
}
put(buffer,OVER);
return NULL;
}
void *consumer(void *data)
{
int d;
while(1)
{
d = get(buffer);
if(d == OVER)
break;
else
printf("-----%d\n",d);
}
return NULL;
}
int main()
{
pthread_t th_p,th_c;
void *retval;
pthread_init(buffer);
pthread_create(th_p,NULL,producer,0);
pthread_create(th_c,NULL,consumer,0);
/*等待两个线程结束*/
pthread_join(th_p, retval);
pthread_join(th_c,retval);
return 0;
}
C语言多线程的操作步骤
线程创建
函数原型:intpthread_create(pthread_t*restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void),void *restrict arg);
返回值:若是成功建立线程返回0,否则返回错误的编号。
形式参数:pthread_t*restrict tidp要创建的线程的线程id指针;const pthread_attr_t *restrict attr创建线程时的线程属性;void *(start_rtn)(void)返回值是void类型的指针函数;void *restrict arg start_rtn的形参。
线程挂起:该函数的作用使得当前线程挂起,等待另一个线程返回才继续执行。也就是说当程序运行到这个地方时,程序会先停止,然后等线程id为thread的这个线程返回,然后程序才会断续执行。
函数原型:intpthread_join(pthread_tthread, void **value_ptr);
参数说明如下:thread等待退出线程的线程号;value_ptr退出线程的返回值。
返回值:若成功,则返回0;若失败,则返回错误号。
线程退出
函数原型:voidpthread_exit(void *rval_ptr);
获取当前线程id
函数原型:pthread_tpthread_self(void);
互斥锁
创建pthread_mutex_init;销毁pthread_mutex_destroy;加锁pthread_mutex_lock;解锁pthread_mutex_unlock。
条件锁
创建pthread_cond_init;销毁pthread_cond_destroy;触发pthread_cond_signal;广播pthread_cond_broadcast;等待pthread_cond_wait。