一、Linux系统中的线程间通信方式主要以下几种:
*
读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
*
*
二、详解
1、互斥锁通信机制
基本原理
互斥锁以排他方式防止共享数据被并发访问,互斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个互斥锁逻辑上绑定之后,对该资源的访问操作如下:
(1)在访问该资源之前需要首先申请互斥锁,如果锁处于开状态,则申请得到锁并立即上锁(关),防止其他进程访问资源,如果锁处于关,则默认阻塞等待.
(2)只有锁定该互斥锁的进程才能释放该互斥锁.
互斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有具体的定义。
互斥量初始化和销毁
在使用互斥锁之前需要定义互斥锁(全局变量),定义代码:
通过锁机制实现线程间的同步。同一时刻只允许一个线程执行一个关键部分的代码。
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_destroy(pthread_mutex *mutex);
int pthread_mutex_unlock(pthread_mutex *
(1)先初始化锁init()或静态赋值pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIER
attr_t有:
PTHREAD_MUTEX_TIMED_NP:其余线程等待队列
PTHREAD_MUTEX_RECURSIVE_NP:嵌套锁,允许线程多次加锁,不同线程,解锁后重新竞争
PTHREAD_MUTEX_ERRORCHECK_NP:检错,与一同,线程请求已用锁,返回EDEADLK;
PTHREAD_MUTEX_ADAPTIVE_NP:适应锁,解锁后重新竞争
(2)加锁,lock,trylock,lock阻塞等待锁,trylock立即返回EBUSY
(3)解锁,unlock需满足是加锁状态,且由加锁线程解锁
(4)清除锁,destroy(此时锁必需unlock,否则返回EBUSY,//Linux下互斥锁不占用内存资源
pthread_mutex lock;
/* Initialize a mutex. */ int pthread_mutex_init (pthread_mutex_t *__mutex,\ // 指向要初始化的互斥锁的指针 __const pthread_mutexattr_t *__mutexattr); // 指向互斥锁属性对象的指针,设null为默认属性 /* Destroy a mutex. */ int pthread_mutex_destroy (pthread_mutex_t *__mutex);
上面两个函数分别由于互斥量的初始化和销毁。
如果互斥量是静态分配的,可以通过常量进行初始化,如下:
#define PTHREAD_MUTEX_INITIALIZER {{0, }} // 系统定义的,无需声明 pthread_mutex_t mlock = PTHREAD_MUTEX_INITIALIZER;
当然也可以通过pthread_mutex_init()进行初始化。对于动态分配的互斥量由于不能直接赋值进行初始化就只能采用这种方式进行初始化,pthread_mutex_init()的第二个参数是互斥量的属性,如果采用默认的属性设置,可以传入NULL。
当不在需要使用互斥量时,需要调用pthread_mutex_destroy()销毁互斥量所占用的资源。
互斥量的属性设置
/* 初始化互斥量属性对象 */ int pthread_mutexattr_init (pthread_mutexattr_t *__attr); /* 销毁互斥量属性对象 */ int pthread_mutexattr_destroy (pthread_mutexattr_t *__attr); /* 获取互斥量属性对象在进程间共享与否的标志 */ int pthread_mutexattr_getpshared (__const pthread_mutexattr_t *__restrict __attr, \ int *__restrict __pshared); /* 设置互斥量属性对象,标识在进程间共享与否 */ int pthread_mutexattr_setpshared (pthread_mutexattr_t *__attr, int __pshared);
互斥量在初始化的时候pthread_mutex_init的第二个参数是互斥量的属性,如果为NULL空指针,那么就使用默认属性。
互斥量属性的数据类型为pthread_mutexattr_t,它的初始化和销毁和互斥量类似。一旦互斥量属性对象被初始化后,就可以通过调用不同的函数启用或禁止特定的属性。这里列出了一个设置特定属性的函数:pthread_mutexattr_setpshared,可以用于指定互斥量在不同进程间共享,这样可以通过互斥量来同步不同的进程,当然前提是该互斥量位于进程间的共享内存区。
pthread_mutexattr_setpshared()函数的第二个参数__pshared用于设定是否进程间共享,其值可以是PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED,后者是设置进程间共享。
下面是使互斥量可以在进程间共享的大致过程:
pthread_mutex_t *pSharedMutex; //指向共享内存区的互斥量 pthread_mutexattr_t mutexAttr; //互斥量属性 pSharedMutex = /*一个指向共享内存区的指针*/; pthread_mutexattr_init(&mutexAttr); pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED); pthread_mutex_init(pSharedMutex, &mutexAttr);
互斥量的申请使用
/* Try locking a mutex. */ int pthread_mutex_trylock (pthread_mutex_t *__mutex); /* Lock a mutex. */ int pthread_mutex_lock (pthread_mutex_t *__mutex); /* Unlock a mutex. */ int pthread_mutex_unlock (pthread_mutex_t *__mutex);这几个函数都很简单,通过pthread_mutex_lock()函数获得访问共享资源的权限,如果已经有其他线程锁住互斥量,那么该函数会是线程阻塞指定该互斥量解锁为止。 pthread_mutex_trylock()是对应的非阻塞函数,如果互斥量已被占用,它会返回一个EBUSY错误。访问完共享资源后,一定要通过pthread_mutex_unlock() 函数,释放占用的互斥量。允许其他线程访问该资源。
这里要强调的是:互斥量是用于上锁的,不能用于等待。
简单说就是,互斥量的使用流程应该是:线程占用互斥量,然后访问共享资源,最后释放互斥量。而不应该是:线程占用互斥量,然后判断资源是否可用,如果不可用,释放互斥量,然后重复上述过程。这种行为称为轮转或轮询,是一种浪费CPU时间的行为。
应用示例
下面的程序使用了两个同进程的线程,一个线程负责从标准设备读入数据存储在共享数据区,另一个负责将读入的数据输出到标准设备.其实是一个生产者消费者的变形.
- 处理输入操作的线程在接受用户信息的时候必须互斥使用该资源,不能被打断,阻塞其他线程的进行
- 输出线程也不能被打断,需要先锁定互斥锁,操作完成后释放,
- 程序结束,销毁互斥锁.
代码如下:
/************************************************************************* > File Name: pthread_exp.c > Author:SuooL > Mail:1020935219@qq.com || hu1020935219@gmail.com > Website:http://blog.csdn.net/suool | http://suool.net > Created Time: 2014年08月15日 星期三 08时42分45秒 > Description: ************************************************************************/ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #include <string.h> void *thread_function(void *arg); pthread_mutex_t work_mutex; // 定义全局互斥锁对象 #define WORK_SIZE 1024 char work_area[WORK_SIZE]; // 全局共享数据区 int time_to_exit = 0; // 标志变量 // main函数 int main(int argc,char *argv[]) { int res; pthread_t a_thread; // 线程 void *thread_result; res = pthread_mutex_init(&work_mutex, NULL); //init mutex 初始化互斥锁 if (res != 0) { perror("Mutex initialization failed"); exit(EXIT_FAILURE); } res = pthread_create(&a_thread, NULL, thread_function, NULL);//create new thread if (res != 0) { perror("Thread creation failed"); exit(EXIT_FAILURE); } pthread_mutex_lock(&work_mutex); //lock the mutex,before get the input printf("Input some text. Enter 'end' to finish\n"); while(!time_to_exit) { fgets(work_area, WORK_SIZE, stdin); //get a string from stdin pthread_mutex_unlock(&work_mutex); //unlock the mutex while(1) { pthread_mutex_lock(&work_mutex); //lock the mutex if (work_area[0] != '\0') // check out if the info input has been output { pthread_mutex_unlock(&work_mutex); //unlock the mutex sleep(1); } else // if has been output, break and goto next iput { break; } } } pthread_mutex_unlock(&work_mutex); // unlock the mutex printf("\nWaiting for thread to finish...\n"); res = pthread_join(a_thread, &thread_result); // wait for the other pthread end if (res != 0) { perror("Thread join failed"); exit(EXIT_FAILURE); } printf("Thread joined\n"); pthread_mutex_destroy(&work_mutex); // destory the mutex exit(EXIT_SUCCESS); } // the pthread run function void *thread_function(void *arg) { sleep(1); pthread_mutex_lock(&work_mutex); // lock the mutex 抢占资源 while(strncmp("end", work_area, 3) != 0) // 判断是否为结束信息 { printf("You input %d characters\n", strlen(work_area) -1); // 输出输入信息 printf("the characters is %s",work_area); work_area[0] = '\0'; // 设置数据区,表示输入已被输出 pthread_mutex_unlock(&work_mutex); // 解锁 sleep(1); pthread_mutex_lock(&work_mutex); // 上锁 while (work_area[0] == '\0' ) // 判断是否被输出 { // 如果是\0则没有输入,等待主进程输入 pthread_mutex_unlock(&work_mutex); // 解锁 sleep(1); // 等待 pthread_mutex_lock(&work_mutex); // 上锁,再次检测 } } time_to_exit = 1; // 设置主线程退出信号 work_area[0] = '\0'; // 标志输出 pthread_mutex_unlock(&work_mutex); // 世界所 pthread_exit(0); // 线程退出 }
运行结果:
2、条件变量
条件变量(cond)
利用线程间共享的全局变量进行同步的一种机制。条件变量上的基本操作有:触发条件(当条件变为 true 时);等待条件,挂起线程直到其他线程触发条件。
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); //解除所有线程的阻塞
(1)初始化.init()或者pthread_cond_t cond=PTHREAD_COND_INITIALIER(前者为动态初始化,后者为静态初始化);属性置为NULL
(2)等待条件成立.pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真,timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
(3)激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
(4)清除条件变量:destroy;无线程等待,否则返回EBUSY
对于
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
一定要在mutex的锁定区域内使用。
如果要正确的使用pthread_mutex_lock与pthread_mutex_unlock,请参考
pthread_cleanup_push和pthread_cleanup_pop宏,它能够在线程被cancel的时候正确的释放mutex!
另外,posix1标准说,pthread_cond_signal与pthread_cond_broadcast无需考虑调用线程是否是mutex的拥有者,也就是说,可以在lock与unlock以外的区域调用。如果我们对调用行为不关心,那么请在lock区域之外调用吧。
说明:
(1)pthread_cond_wait 自动解锁互斥量(如同执行了pthread_unlock_mutex),并等待条件变量触发。这时线程挂起,不占用CPU时间,直到条件变量被触发(变量为ture)。在调用 pthread_cond_wait之前,应用程序必须加锁互斥量。pthread_cond_wait函数返回前,自动重新对互斥量加锁(如同执行了pthread_lock_mutex)。
(2)互斥量的解锁和在条件变量上挂起都是自动进行的。因此,在条件变量被触发前,如果所有的线程都要对互斥量加锁,这种机制可保证在线程加锁互斥量和进入等待条件变量期间,条件变量不被触发。条件变量要和互斥量相联结,以避免出现条件竞争——个线程预备等待一个条件变量,当它在真正进入等待之前,另一个线程恰好触发了该条件(条件满足信号有可能在测试条件和调用pthread_cond_wait函数(block)之间被发出,从而造成无限制的等待)。
(3)pthread_cond_timedwait 和 pthread_cond_wait 一样,自动解锁互斥量及等待条件变量,但它还限定了等待时间。如果在abstime指定的时间内cond未触发,互斥量mutex被重新加锁,且pthread_cond_timedwait返回错误 ETIMEDOUT。abstime 参数指定一个绝对时间,时间原点与 time 和 gettimeofday 相同:abstime = 0 表示 1970年1月1日00:00:00 GMT。
(4)pthread_cond_destroy 销毁一个条件变量,释放它拥有的资源。进入 pthread_cond_destroy 之前,必须没有在该条件变量上等待的线程。
(5)条件变量函数不是异步信号安全的,不应当在信号处理程序中进行调用。特别要注意,如果在信号处理程序中调用 pthread_cond_signal 或pthread_cond_boardcast 函数,可能导致调用线程死锁。
示例程序1
- #include <stdio.h>
- #include <pthread.h>
- #include "stdlib.h"
- #include "unistd.h"
- pthread_mutex_t mutex;
- pthread_cond_t cond;
- void hander(void *arg)
- {
- free(arg);
- (void)pthread_mutex_unlock(&mutex);
- }
- void *thread1(void *arg)
- {
- pthread_cleanup_push(hander, &mutex);
- while(1)
- {
- printf("thread1 is running\n");
- pthread_mutex_lock(&mutex);
- pthread_cond_wait(&cond,&mutex);
- printf("thread1 applied the condition\n");
- pthread_mutex_unlock(&mutex);
- sleep(4);
- }
- pthread_cleanup_pop(0);
- }
- void *thread2(void *arg)
- {
- while(1)
- {
- printf("thread2 is running\n");
- pthread_mutex_lock(&mutex);
- pthread_cond_wait(&cond,&mutex);
- printf("thread2 applied the condition\n");
- pthread_mutex_unlock(&mutex);
- sleep(1);
- }
- }
- int main()
- {
- pthread_t thid1,thid2;
- printf("condition variable study!\n");
- pthread_mutex_init(&mutex,NULL);
- pthread_cond_init(&cond,NULL);
- pthread_create(&thid1,NULL,thread1,NULL);
- pthread_create(&thid2,NULL,thread2,NULL);
- sleep(1);
- do
- {
- pthread_cond_signal(&cond);
- }while(1);
- sleep(20);
- pthread_exit(0);
- return 0;
- }
示例程序2:
- #include <pthread.h>
- #include <unistd.h>
- #include "stdio.h"
- #include "stdlib.h"
- static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
- static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
- struct node
- {
- int n_number;
- struct node *n_next;
- } *head = NULL;
- /*[thread_func]*/
- static void cleanup_handler(void *arg)
- {
- printf("Cleanup handler of second thread./n");
- free(arg);
- (void)pthread_mutex_unlock(&mtx);
- }
- static void *thread_func(void *arg)
- {
- struct node *p = NULL;
- pthread_cleanup_push(cleanup_handler, p);
- while (1)
- {
- //这个mutex主要是用来保证pthread_cond_wait的并发性
- pthread_mutex_lock(&mtx);
- while (head == NULL)
- {
- //这个while要特别说明一下,单个pthread_cond_wait功能很完善,为何
- //这里要有一个while (head == NULL)呢?因为pthread_cond_wait里的线
- //程可能会被意外唤醒,如果这个时候head != NULL,则不是我们想要的情况。
- //这个时候,应该让线程继续进入pthread_cond_wait
- // pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,
- //然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立
- //而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);,再读取资源
- //用这个流程是比较清楚的/*block-->unlock-->wait() return-->lock*/
- pthread_cond_wait(&cond, &mtx);
- p = head;
- head = head->n_next;
- printf("Got %d from front of queue/n", p->n_number);
- free(p);
- }
- pthread_mutex_unlock(&mtx); //临界区数据操作完毕,释放互斥锁
- }
- pthread_cleanup_pop(0);
- return 0;
- }
- int main(void)
- {
- pthread_t tid;
- int i;
- struct node *p;
- //子线程会一直等待资源,类似生产者和消费者,但是这里的消费者可以是多个消费者,而
- //不仅仅支持普通的单个消费者,这个模型虽然简单,但是很强大
- pthread_create(&tid, NULL, thread_func, NULL);
- sleep(1);
- for (i = 0; i < 10; i++)
- {
- p = (struct node*)malloc(sizeof(struct node));
- p->n_number = i;
- pthread_mutex_lock(&mtx); //需要操作head这个临界资源,先加锁,
- p->n_next = head;
- head = p;
- pthread_cond_signal(&cond);
- pthread_mutex_unlock(&mtx); //解锁
- sleep(1);
- }
- printf("thread 1 wanna end the line.So cancel thread 2./n");
- //关于pthread_cancel,有一点额外的说明,它是从外部终止子线程,子线程会在最近的取消点,退出
- //线程,而在我们的代码里,最近的取消点肯定就是pthread_cond_wait()了。
- pthread_cancel(tid);
- pthread_join(tid, NULL);
- printf("All done -- exiting/n");
- return 0;
- }
读写锁通信机制
读写锁和互斥量(互斥锁)很类似,是另一种线程同步机制,但不属于POSIX标准,可以用来同步同一进程中的各个线程。当然如果一个读写锁存放在多个进程共享的某个内存区中,那么还可以用来进行进程间的同步.
因为对数据的读写应用中,很多情况都是大量的读操作,而较少有写操作,例如对数据库的访问等,显然这样使用互斥锁会很影响效率,为了满足这样的需求,POSIX线提供了读写锁机制.模式如下:
- 如果当前进程读数据,其他的进程也可以进行读操作,但不能写操作
- 如果当前进程写数据,则所有其它进程阻塞等待
和互斥量不同的是:互斥量会把试图进入已保护的临界区的线程都阻塞;然而读写锁会视当前进入临界区的线程和请求进入临界区的线程的属性来判断是否允许线程进入。
相对互斥量只有加锁和不加锁两种状态,读写锁有三种状态:读模式下的加锁,写模式下的加锁,不加锁。
读写锁的使用规则:
- 只要没有写模式下的加锁,任意线程都可以进行读模式下的加锁;
- 只有读写锁处于不加锁状态时,才能进行写模式下的加锁;
读写锁也称为共享-独占(shared-exclusive)锁,当读写锁以读模式加锁时,它是以共享模式锁住,当以写模式加锁时,它是以独占模式锁住。读写锁非常适合读数据的频率远大于写数据的频率从的应用中。这样可以在任何时刻运行多个读线程并发的执行,给程序带来了更高的并发度。
定义代码如下:
pthread_rwlock_t ralock;
初始化和销毁读写锁
/* Initialize read-write lock */ int pthread_rwlock_init (pthread_rwlock_t *__restrict __rwlock, // 指向要初始化的读写锁指针 __const pthread_rwlockattr_t *__restrict __attr); // 指向属性对象的指针 /* Destroy read-write lock */ extern int pthread_rwlock_destroy (pthread_rwlock_t *__rwlock); //返回值:成功返回0,否则返回错误代码
上面两个函数分别由于读写锁的初始化和销毁。和互斥量,条件变量一样,如果读写锁是静态分配的,可以通过常量进行初始化,如下:
<span style="font-size:12px;">pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;</span>
也可以通过pthread_rwlock_init()进行初始化。对于动态分配的读写锁由于不能直接赋值进行初始化,只能通过这种方式进行初始化。pthread_rwlock_init()第二个参数是读写锁的属性,如果采用默认属性,可以传入空指针NULL。
那么当不在需要使用时及释放(自动或者手动)读写锁占用的内存之前,需要调用 pthread_rwlock_destroy() 进行销毁读写锁占用的资源。读写锁的属性设置
/* 初始化读写锁属性对象 */ int pthread_rwlockattr_init (pthread_rwlockattr_t *__attr); /* 销毁读写锁属性对象 */ int pthread_rwlockattr_destroy (pthread_rwlockattr_t *__attr); /* 获取读写锁属性对象在进程间共享与否的标识*/ int pthread_rwlockattr_getpshared (__const pthread_rwlockattr_t * __restrict __attr, int *__restrict __pshared); /* 设置读写锁属性对象,标识在进程间共享与否 */ int pthread_rwlockattr_setpshared (pthread_rwlockattr_t *__attr, int __pshared); 返回值:成功返回0,否则返回错误代码
这个属性设置和互斥量的基本一样,具体可以参考上面的互斥量的设置 互斥量的属性设置读写锁的申请使用
/* 读模式下加锁 */ int pthread_rwlock_rdlock (pthread_rwlock_t *__rwlock); /* 非阻塞的读模式下加锁 */ int pthread_rwlock_tryrdlock (pthread_rwlock_t *__rwlock); # ifdef __USE_XOPEN2K /* 限时等待的读模式加锁 */ int pthread_rwlock_timedrdlock (pthread_rwlock_t *__restrict __rwlock, __const struct timespec *__restrict __abstime); # endif /* 写模式下加锁 */ int pthread_rwlock_wrlock (pthread_rwlock_t *__rwlock); /* 非阻塞的写模式下加锁 */ int pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock); # ifdef __USE_XOPEN2K /* 限时等待的写模式加锁 */ int pthread_rwlock_timedwrlock (pthread_rwlock_t *__restrict __rwlock, __const struct timespec *__restrict __abstime); # endif /* 解锁 */ int pthread_rwlock_unlock (pthread_rwlock_t *__rwlock); 返回值:成功返回0,否则返回错误代码
(1)pthread_rwlock_rdlock()系列函数pthread_rwlock_rdlock()用于以读模式即共享模式获取读写锁,如果读写锁已经被某个线程以写模式占用,那么调用线程就被阻塞。在实现读写锁的时候可以对共享模式下锁的数量进行限制(目前不知如何限制)。
pthread_rwlock_tryrdlock()和pthread_rwlock_rdlock()的唯一区别就是,在无法获取读写锁的时候,调用线程不会阻塞,会立即返回,并返回错误代码EBUSY。
pthread_rwlock_timedrdlock()是限时等待读模式加锁,时间参数struct timespec * __restrict __abstime也是绝对时间,和条件变量的pthread_cond_timedwait()使用基本一致,具体可以参考pthread_cond_timedwait()
(2)pthread_rwlock_wrlock()系列函数
pthread_rwlock_wrlock()用于写模式即独占模式获取读写锁,如果读写锁已经被其他线程占用,不论是以共享模式还是独占模式占用,调用线程都会进入阻塞状态。
pthread_rwlock_trywrlock()在无法获取读写锁的时候,调用线程不会进入睡眠,会立即返回,并返回错误代码EBUSY。
pthread_rwlock_timedwrlock()是限时等待写模式加锁,也和条件变量的pthread_cond_timedwait()使用基本一致,具体可以参考pthread_cond_timedwait()
(3)pthread_rwlock_unlock()
无论以共享模式还是独占模式获得的读写锁,都可以通过调用pthread_rwlock_unlock()函数进行释放该读写锁。
读写锁应用
下面的程序使用读写锁实现4个线程读写一段数据.
其中两个线程读数据,两个线程写数据.
从结果看出,任意进程写数据的时候,将阻塞所有其他进程的操作,但在某一进程读数据的时候,其他进程仍然可以读得数据.
编译运行结果如下:
代码如下:
/************************************************************************* > File Name: pthread_rwlock_exp.c > Author:SuooL > Mail:1020935219@qq.com || hu1020935219@gmail.com > Website:http://blog.csdn.net/suool | http://suool.net > Created Time: 2014年08月15日 星期三 09时20分45秒 > Description: ************************************************************************/ #include <errno.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <bits/pthreadtypes.h> static pthread_rwlock_t rwlock; // 读写锁对象 #define WORK_SIZE 1024 char work_area[WORK_SIZE]; // 全局共享数据区 int time_to_exit; // 标志变量 // 子线程执行的函数声明 void *thread_function_read_o(void *arg); void *thread_function_read_t(void *arg); void *thread_function_write_o(void *arg); void *thread_function_write_t(void *arg); // main 函数 int main(int argc,char *argv[]) { int res; pthread_t a_thread,b_thread,c_thread,d_thread; // 线程定义 void *thread_result; // 指针定义 res=pthread_rwlock_init(&rwlock,NULL); // 读写锁定义 if (res != 0) { perror("rwlock initialization failed"); exit(EXIT_FAILURE); } // 创建子线程 res = pthread_create(&a_thread, NULL, thread_function_read_o, NULL);//create new thread if (res != 0) { perror("Thread creation failed"); exit(EXIT_FAILURE); } res = pthread_create(&b_thread, NULL, thread_function_read_t, NULL);//create new thread if (res != 0) { perror("Thread creation failed"); exit(EXIT_FAILURE); } res = pthread_create(&c_thread, NULL, thread_function_write_o, NULL);//create new thread if (res != 0) { perror("Thread creation failed"); exit(EXIT_FAILURE); } res = pthread_create(&d_thread, NULL, thread_function_write_t, NULL);//create new thread if (res != 0) { perror("Thread creation failed"); exit(EXIT_FAILURE); } // 线程等待 res = pthread_join(a_thread, &thread_result); if (res != 0) { perror("Thread join failed"); exit(EXIT_FAILURE); } res = pthread_join(b_thread, &thread_result); if (res != 0) { perror("Thread join failed"); exit(EXIT_FAILURE); } res = pthread_join(c_thread, &thread_result); if (res != 0) { perror("Thread join failed"); exit(EXIT_FAILURE); } res = pthread_join(d_thread, &thread_result); if (res != 0) { perror("Thread join failed"); exit(EXIT_FAILURE); } // 读写锁销毁 pthread_rwlock_destroy(&rwlock); exit(EXIT_SUCCESS); } void *thread_function_read_o(void *arg) // 读线程one { printf("thread read one try to get lock\n"); pthread_rwlock_rdlock(&rwlock); // 获取读写锁 while(strncmp("end", work_area, 3) != 0) // 判断是否为输入结束符 { printf("this is thread read one.\n"); // 输出信息 printf("the characters is %s",work_area); pthread_rwlock_unlock(&rwlock); // 解锁 sleep(2); pthread_rwlock_rdlock(&rwlock); // 获取所 while (work_area[0] == '\0' ) // 查看数据区开头是否为\0 { pthread_rwlock_unlock(&rwlock); // 解锁 sleep(2); // 等待 pthread_rwlock_rdlock(&rwlock); // 获取锁,再次查看 } } pthread_rwlock_unlock(&rwlock); // 解锁 time_to_exit=1; // 设置退出信号 pthread_exit(0); // 线程退出 } void *thread_function_read_t(void *arg) // 读线程two,同one { printf("thread read one try to get lock\n"); pthread_rwlock_rdlock(&rwlock); while(strncmp("end", work_area, 3) != 0) { printf("this is thread read two.\n"); printf("the characters is %s\n",work_area); pthread_rwlock_unlock(&rwlock); sleep(5); pthread_rwlock_rdlock(&rwlock); while (work_area[0] == '\0' ) { pthread_rwlock_unlock(&rwlock); sleep(5); pthread_rwlock_rdlock(&rwlock); } } pthread_rwlock_unlock(&rwlock); time_to_exit=1; pthread_exit(0); } void *thread_function_write_o(void *arg) // 写线程one { printf("this is write thread one try to get lock\n"); // 输出提示信息 while(!time_to_exit) // 是否退出 { pthread_rwlock_wrlock(&rwlock); // 获取读写锁 printf("this is write thread one.\nInput some text. Enter 'end' to finish\n"); fgets(work_area, WORK_SIZE, stdin); // 获取标准输入写到是数据区 pthread_rwlock_unlock(&rwlock); // 解锁 sleep(15); // 等待 } pthread_rwlock_unlock(&rwlock); // 解锁 pthread_exit(0); // 线程退出 } void *thread_function_write_t(void *arg) // 写线程two,同one { sleep(10); while(!time_to_exit) { pthread_rwlock_wrlock(&rwlock); printf("this is write thread two.\nInput some text. Enter 'end' to finish\n"); fgets(work_area, WORK_SIZE, stdin); pthread_rwlock_unlock(&rwlock); sleep(20); } pthread_rwlock_unlock(&rwlock); pthread_exit(0); }
4、信号量
如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。
信号量函数的名字都以"sem_"打头。线程使用的基本信号量函数有四个。
#include <semaphore.h>
int sem_init (sem_t *sem , int pshared, unsigned int value);
这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。
两个原子操作函数:
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。
sem_post:给信号量的值加1;
sem_wait:给信号量减1;对一个值为0的信号量调用sem_wait,这个函数将会等待直到有其它线程使它不再是0为止。
int sem_destroy(sem_t *sem);
这个函数的作用是再我们用完信号量后都它进行清理。归还自己占有的一切资源。
示例代码:
- #include <stdlib.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <pthread.h>
- #include <semaphore.h>
- #include <errno.h>
- #define return_if_fail(p) if((p) == 0){printf ("[%s]:func error!/n", __func__);return;}
- typedef struct _PrivInfo
- {
- sem_t s1;
- sem_t s2;
- time_t end_time;
- }PrivInfo;
- static void info_init (PrivInfo* thiz);
- static void info_destroy (PrivInfo* thiz);
- static void* pthread_func_1 (PrivInfo* thiz);
- static void* pthread_func_2 (PrivInfo* thiz);
- int main (int argc, char** argv)
- {
- pthread_t pt_1 = 0;
- pthread_t pt_2 = 0;
- int ret = 0;
- PrivInfo* thiz = NULL;
- thiz = (PrivInfo* )malloc (sizeof (PrivInfo));
- if (thiz == NULL)
- {
- printf ("[%s]: Failed to malloc priv./n");
- return -1;
- }
- info_init (thiz);
- ret = pthread_create (&pt_1, NULL, (void*)pthread_func_1, thiz);
- if (ret != 0)
- {
- perror ("pthread_1_create:");
- }
- ret = pthread_create (&pt_2, NULL, (void*)pthread_func_2, thiz);
- if (ret != 0)
- {
- perror ("pthread_2_create:");
- }
- pthread_join (pt_1, NULL);
- pthread_join (pt_2, NULL);
- info_destroy (thiz);
- return 0;
- }
- static void info_init (PrivInfo* thiz)
- {
- return_if_fail (thiz != NULL);
- thiz->end_time = time(NULL) + 10;
- sem_init (&thiz->s1, 0, 1);
- sem_init (&thiz->s2, 0, 0);
- return;
- }
- static void info_destroy (PrivInfo* thiz)
- {
- return_if_fail (thiz != NULL);
- sem_destroy (&thiz->s1);
- sem_destroy (&thiz->s2);
- free (thiz);
- thiz = NULL;
- return;
- }
- static void* pthread_func_1 (PrivInfo* thiz)
- {
- return_if_fail (thiz != NULL);
- while (time(NULL) < thiz->end_time)
- {
- sem_wait (&thiz->s2);
- printf ("pthread1: pthread1 get the lock./n");
- sem_post (&thiz->s1);
- printf ("pthread1: pthread1 unlock/n");
- sleep (1);
- }
- return;
- }
- static void* pthread_func_2 (PrivInfo* thiz)
- {
- return_if_fail (thiz != NULL);
- while (time (NULL) < thiz->end_time)
- {
- sem_wait (&thiz->s1);
- printf ("pthread2: pthread2 get the unlock./n");
- sem_post (&thiz->s2);
- printf ("pthread2: pthread2 unlock./n");
- sleep (1);
- }
- return;
- }
通 过执行结果后,可以看出,会先执行线程二的函数,然后再执行线程一的函数。它们两就实现了同步。在上大学的时候,虽然对这些概念知道,可都没有实践过,所 以有时候时间一久就会模糊甚至忘记,到了工作如果还保持这么一种状态,那就太可怕了。虽然现在外面的技术在不断的变化更新,可是不管怎么变,其核心技术还 是依旧的,所以我们必须要打好自己的基础,再学习其他新的知识,那时候再学新的知识也会觉得比较简单的。