基本概念
在程序中一些任务需要多个线程来完成,不同的线程之间存在相互制约关系。为了协调线程之间的制约关系引入了线程同步的概念(线程间的通信主要是用于线程同步,所以线程没有进程通信中用于数据交换的通信机制)。线程同步是指按特定的机制(如锁 信号量)来控制线程之间的执行顺序。如果没有线程同步,那线程之间将是无序的。
线程同步的核心思想就是在访问临界区的时候只允许一个或一类线程访问
临界区
多个线程访问或操作同一块区域(代码),这块就称为临界区。
同步机制
嵌入式多线程中主要有三种同步机制:互斥锁、信号量、条件量。
互斥锁
互斥锁用来保证同一时间只有一个线程在执行某段代码(临界区)
互斥锁主要包括下面的基本函数
互斥锁初始化:pthread_mutex_init()
互斥锁上锁:pthread_mutex_lock()
互斥锁判断上锁:pthread_mutex_trylock()
互斥锁解锁:pthread_mutex_unlock()
消除互斥锁:pthread_mutex_destroy()
信号量
多线程中也可以使用信号量来进行数据同步
- 信号量初始化
int sem_init (sem_t *sem , int pshared, unsigned int value);
对sem指定的信号量进行初始化,设置好共享选项(linux 只支持为0 即表示它是当前进程的局部变量),然后给它一个初始化值VALUE - 等待信号量
int sem_wait(sem_t *sem);
给信号量减1.然后等待知道信号量的值大于0 - 释放信号量
int sem_post(sem_t *sem);
信号量值加1,并通知其他等待线程 - 销毁信号量
int sem_destroy(sem_t *sem);
程序实例:
使用生产者 消费者的经典例子,生产者,消费者共用一个缓冲区,缓冲区存放多条消息。使用信号量如下:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>
static char buff[50];
pthread_mutex_t mutex;
pthread_mutex_t cond_mutex;
sem_t msg_cnt; //缓冲区消息数
sem_t idle_cnt // 缓冲区空闲数
void consumItem(cahr *buff)
{
printf("This is a consum item\n");
}
void produceItem(char *buff)
{
printf("This is a produce item\n");
}
void *consumer(void *param)
{
while (1)
{
sem_wait(&msg_cnt);
pthread_mutex_lock(&mutex);
consumeItem(buff);
pthread_mutex_unlock(&mutex);
sem_post(&idle_cnt);
}
return NULL;
}
void *producer(void *param)
{
while (1)
{
sem_wait(&idle_cnt);
pthread_mutex_lock(&mutex);
produceItem(buff);
pthread_mutex_unlock(&mutex);
sem_post(&msg_cnt);
}
return NULL;
}
int main()
{
pthread_t tid_c, tid_p;
void *retval;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_init(&cond_mutex, NULL);
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
sem_init(&msg_cnt, 0, 0); //初始缓冲区没有消息
sem_init(&idle_cnt, 0, 10); //初始缓冲区能放10条消息
pthread_create(&tid_p, NULL, producer, NULL);
pthread_create(&tid_c, NULL, consumer, NULL);
pthread_join(tid_p, &retval);
pthread_join(tid_c, &retval);
return 0;
}
条件变量
与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量分 为两部分: 条件和变量。条件本身是由互斥量保护的。线程在改变条件状态前先要锁住互斥量。条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局 变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。条件的检测是在 互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。
条件变量用来阻塞线程等待某个事件的发生,并且当等待的事件发生时,阻塞线程会被通知。互斥锁的缺点是只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线承间的同步
- 初始化条件变量
静态初始化 prthead_cond_t cond = PTHREAD_COND_INITIALER
动态初始化:ead_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); - 等待条件成立
释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int thread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime); - 激活条件变量
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond); 解除所有线程的阻塞 - 清除条件变量
无线程等待,否则返回EBUSY
int pthread_cond_destroy(pthread_cond_t *cond); - 程序实例
本实例使用条件变量实现生产者、消费者的经典例子,生产者、消费者公用一个缓冲区,缓冲区存放多条消息,有多个消费者。如果都使用互斥锁,那么多个消费者线程都要不断的去查看缓冲区是否有消息,有就取走。如果我们用条件变量,多个消费者线程都可以放心的“睡觉”,缓冲区有消息,消费者会收到通知,进而收取消息就行。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
static char buff[50];
pthread_mutex_t mutex;
pthread_mutex_t cond_mutex;
pthread_cond_t cond;
void consumeItem(char *buff)
{
printf("This is a consumer item\n");
}
void produceItem(char *buff)
{
printf("This is a produce item\n");
}
void *consumer(void *param)
{
int t = *(int *)param;
while (1)
{
pthread_cond_wait(&cond, &cond_mutex);
pthread_mutex_lock(&mutex);
printf("consumer thread %d: ", t);
consumeItem(buff);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *producer(void *param)
{
while (1)
{
pthread_mutex_lock(&mutex);
produceItem(buff);
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
sleep(1);
}
return NULL;
}
int main(int argc, char **argv)
{
pthread_t tid_c1, tid_p, tid_c2, tid_c3;
void *retval;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_init(&cond_mutex, NULL);
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
int p[3] = {1, 2, 3}; //线程编号
pthread_create(&tid_p, NULL, producer, NULL);
pthread_create(&tid_c1, NULL, consumer, &p[0]);
pthread_create(&tid_c2, NULL, consumer, &p[1]);
pthread_create(&tid_c3, NULL, consumer, &p[2]);
pthread_join(tid_p, &retval);
pthread_join(tid_c1, &retval);
pthread_join(tid_c2, &retval);
pthread_join(tid_c3, &retval);
return 0;
}
转载:
[1]: https://blog.51cto.com/quantfabric/1827303