所谓的同步并不是多个线程同时对内存进行访问,而是按照先后顺序依次进行的。线程同步的经典模型是生产者和消费者模型,读者写者问题,哲学家就餐问题。
C语言实现的生产者和消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
// 链表的节点
struct Node
{
int number;
struct Node* next;
};
// 定义条件变量, 控制消费者线程
pthread_cond_t cond;
// 互斥锁变量
pthread_mutex_t mutex;
// 指向头结点的指针
struct Node * head = NULL;
// 生产者的回调函数
void* producer(void* arg)
{
// 一直生产
while(1)
{
pthread_mutex_lock(&mutex);
// 创建一个链表的新节点
struct Node* pnew = (struct Node*)malloc(sizeof(struct Node));
// 节点初始化
pnew->number = rand() % 1000;
// 节点的连接, 添加到链表的头部, 新节点就新的头结点
pnew->next = head;
// head指针前移
head = pnew;
printf("+++producer, number = %d, tid = %ld\n", pnew->number, pthread_self());
pthread_mutex_unlock(&mutex);
// 生产了任务, 通知消费者消费
pthread_cond_broadcast(&cond);
// 生产慢一点
sleep(rand() % 3);
}
return NULL;
}
// 消费者的回调函数
void* consumer(void* arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
// 一直消费, 删除链表中的一个节点
// if(head == NULL) // 这样写有bug
while(head == NULL)
{
// 任务队列, 也就是链表中已经没有节点可以消费了
// 消费者线程需要阻塞
// 线程加互斥锁成功, 但是线程阻塞在这行代码上, 锁还没解开
// 其他线程在访问这把锁的时候也会阻塞, 生产者也会阻塞 ==> 死锁
// 这函数会自动将线程拥有的锁解开
pthread_cond_wait(&cond, &mutex);
// 当消费者线程解除阻塞之后, 会自动将这把锁锁上
// 这时候当前这个线程又重新拥有了这把互斥锁
}
// 取出链表的头结点, 将其删除
struct Node* pnode = head;
printf("--consumer: number: %d, tid = %ld\n", pnode->number, pthread_self());
head = pnode->next;
free(pnode);
pthread_mutex_unlock(&mutex);
sleep(rand() % 3);
}
return NULL;
}
int main()
{
// 初始化条件变量
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
// 创建5个生产者, 5个消费者
pthread_t ptid[5];
pthread_t ctid[5];
for(int i=0; i<5; ++i)
{
pthread_create(&ptid[i], NULL, producer, NULL);
}
for(int i=0; i<5; ++i)
{
pthread_create(&ctid[i], NULL, consumer, NULL);
}
// 释放资源
for(int i=0; i<5; ++i)
{
// 阻塞等待子线程退出
pthread_join(ptid[i], NULL);
}
for(int i=0; i<5; ++i)
{
pthread_join(ctid[i], NULL);
}
// 销毁条件变量
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
在此基础上实现 C 语言版的handler 机制:
handler.h
//
// Created by xshx on 2021/5/12.
//
#ifndef HANDLERTHREAD_HANDLER_H
#define HANDLERTHREAD_HANDLER_H
#include <pthread.h>
typedef void*(*fun)(void *);
typedef struct Message
{
int messagID;
fun func;
void* arg;
Message* next;
}Message;
class Handler {
public:
// 定义条件变量, 控制消费者线程
pthread_cond_t cond;
// 互斥锁变量
pthread_mutex_t mutex;
// 指向头结点的指针
Message * head;
// 线程id
pthread_t pid=0;
Handler();
void sendMessage(Message *message);
static void* waitMessage(void *arg);
void dispatchMessage();
virtual void handleMessage();
};
#endif //HANDLERTHREAD_HANDLER_H
handler.cpp
//
// Created by xshx on 2021/5/12.
//
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "Handler.h"
Handler::Handler() {
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
// 初始化条件变量
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
pthread_create(&pid, NULL, waitMessage, this);
}
void* Handler::waitMessage(void *arg)
{
Handler* pThis =(Handler*)arg;
pThis->dispatchMessage();
}
void Handler::dispatchMessage( )
{
while(1)
{
pthread_mutex_lock(&mutex);
// 一直消费, 删除链表中的一个节点
// if(head == NULL) // 这样写有bug
while( head == NULL)
{
// 任务队列, 也就是链表中已经没有节点可以消费了
// 消费者线程需要阻塞
// 线程加互斥锁成功, 但是线程阻塞在这行代码上, 锁还没解开
// 其他线程在访问这把锁的时候也会阻塞, 生产者也会阻塞 ==> 死锁
// 这函数会自动将线程拥有的锁解开
pthread_cond_wait(&cond, &mutex);
// 当消费者线程解除阻塞之后, 会自动将这把锁锁上
// 这时候当前这个线程又重新拥有了这把互斥锁
}
// 取出链表的头结点, 将其删除
Message* pnode = head;
printf("--dispatch Message: MessageID: %d, tid = %ld\n", pnode->messagID, pthread_self());
pnode->func( pnode->arg );
head = pnode->next;
free(pnode);
pthread_mutex_unlock(&mutex);
sleep(rand() % 3);
}
return ;
}
void Handler::sendMessage(Message *msg)
{
printf("%s \n", __func__);
// 一直生产
pthread_mutex_lock(&mutex);
// 创建一个链表的新节点
Message* pnew = (Message*)malloc(sizeof(Message));
// 节点初始化
memcpy( pnew, msg, sizeof(Message));
// 节点的连接, 添加到链表的头部, 新节点就新的头结点
pnew->next = head;
// head指针前移
head = pnew;
printf("++sendMessage, messageID = %d, tid = %ld\n", pnew->messagID, pthread_self());
pthread_mutex_unlock(&mutex);
// 生产了任务, 通知消费者消费
pthread_cond_broadcast(&cond);
// 生产慢一点
sleep(rand() % 3);
return ;
}
在main.cpp 文件中 我们可以这么使用。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include "Handler.h"
void* function(void *arg){
char * str = (char *)arg;
printf("%s \n", str);
}
int main()
{
// HandlerThread *thread = new HandlerThread();
char *hello = "hello handler";
Handler *handler = new Handler();
Message message;
message.messagID=2000;
message.func = function;
message.arg = hello;
handler->sendMessage( &message);
return 0;
}
这里实现的handler其实类似HandlerThread。
参考文章:
https://subingwen.cn/linux/thread-sync/
https://blog.csdn.net/hehao13956455/article/details/84638879