互斥锁
互斥锁是线程之间最基本的同步形式,用于保护临界区,任意时刻只能有一个线程在临界区中执行。
初始化
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr);
mutex
:初始化锁的标识符attr
:锁的属性,如果设置为NULL
,则使用系统默认的
加锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
mutex
:锁的标识符
lock
作用是给数据加锁,如果已经有线程给当前数据块加锁了,那么当前线程阻塞,直到上一个线程给代码解锁,然后当前线程唤醒,继续加锁。注意唤醒的过程是自动的,不需要写代码执行。
try_lock
是lock
的非阻塞形式,如果成功加锁,返回0;失败返回有关的错误码。
解锁
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
mutex
:锁的标识符
对加锁的数据进行解锁。
条件变量
经典的wait
和signal
操作,具体参照操作系统,在这里仅学习Linux\Unix中的操作。
初始化和销毁条件变量
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
cond
:条件变量的标识符attr
:条件变量的属性,设置为NULL
则使用系统默认的
wait操作
#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *cond,
pthread_mutex_t *mutex,
const struct timespec *abstime);
int pthread_cond_wait(pthread_cond_t *cond,
pthread_mutex_t *mutex);
cond
:条件变量mutex
:互斥锁,需要进行wait
的代码片段abstime
:等待时间
signal操作
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
cond
:条件变量标识符
signal
表示唤醒一个阻塞的线程,broad_cast
表示唤醒所有的阻塞线程。如果唤醒一个,则根据线程优先级和有关的调度策略执行操作。
一些其它的
线程的属性、互斥量的属性、条件变量的属性可以通过有关的函数进行设置,具体参照手册。
代码实例
生产者线程向缓冲池中填充数据,消费者线程从缓冲区中读取数据。参照《Unix网络编程第二卷》,设计了一个新的结构。要明确,因为互斥量和锁必须同时出现,所以专门设计了一个结构Ready
用于wait
和signal
操作。但是生产者不用进行wait
和signal
操作,只需要添加货物的时候进行互斥,所以单独提取出来。需要注意的是,一定要记得解锁操作,否则会发生死锁阻塞。
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <random>
#include <queue>
// 随机数引擎
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> disCargo(0, 20);
std::uniform_int_distribution<> disTime(0, 3);
const int CONSUMER_NUM = 5;
const int PRODUCER_NUM = 3;
const int CARGO_NUM = 10;
bool stop = false;
std::vector<pthread_t> consumerThreads;
std::vector<pthread_t> producerThreads;
// 生产者放置货物使用的互斥量
struct Cargo {
pthread_mutex_t mutex;
std::queue<int> buffer;
} cargoTag;
// 消费者可以消费的条件变量
struct Ready {
pthread_mutex_t mutex;
pthread_cond_t cond;
} readyTag;
void *consume(void *);
void *produce(void *);
int main() {
pthread_mutex_init(&cargoTag.mutex, nullptr);
pthread_mutex_init(&readyTag.mutex, nullptr);
pthread_cond_init(&readyTag.cond, nullptr);
for (int i = 0; i < PRODUCER_NUM; ++i) {
pthread_t num;
pthread_create(&num, nullptr, &produce, &i);
producerThreads.push_back(num);
}
for (int i = 0; i < CONSUMER_NUM; ++i) {
pthread_t num;
pthread_create(&num, nullptr, &consume, &i);
consumerThreads.push_back(num);
}
for (const auto &it: producerThreads) {
pthread_join(it, nullptr);
}
for (const auto &it: consumerThreads) {
pthread_join(it, nullptr);
}
return 0;
}
void *produce(void *arg) {
pthread_t num = *(pthread_t *) arg;
while (!stop) {
int t = disTime(gen);
int no = disCargo(gen);
sleep(t); // 模拟生产使用的时间
// 生产货物加锁,一定要注意顺序,防止出现死锁
pthread_mutex_lock(&readyTag.mutex);
pthread_mutex_lock(&cargoTag.mutex);
printf("producer %ld produce cargo %d use %d s\n", (long) num, no, t);
cargoTag.buffer.push(no);
pthread_mutex_unlock(&cargoTag.mutex);
pthread_mutex_unlock(&readyTag.mutex);
pthread_cond_signal(&readyTag.cond); // 通知消费者
}
}
void *consume(void *arg) {
pthread_t num = *(pthread_t *) arg;
while (!stop) {
pthread_mutex_lock(&readyTag.mutex);
if (cargoTag.buffer.empty()) {
pthread_mutex_unlock(&readyTag.mutex); // 一定要先解锁
pthread_cond_wait(&readyTag.cond, &readyTag.mutex);
}
int no = cargoTag.buffer.front();
cargoTag.buffer.pop();
pthread_mutex_unlock(&readyTag.mutex); // 一定要解锁
int t = disTime(gen);
sleep(t);
printf("consumer %ld consume cargo %d use %d s\n", (long) num, no, t);
}
}
补充一个C++11的代码实例:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <unistd.h>
#include <random>
#include <queue>
#include <signal.h>
#include <cstring>
// 随机数引擎
std::random_device rd;
std::mt19937 gen (rd() );
std::uniform_int_distribution<> disCargo (0, 20);
std::uniform_int_distribution<> disTime (0, 3);
std::vector<std::thread> producerThreads;
std::vector<std::thread> consumerThreads;
struct Buffers {
std::queue<int> buf;
std::mutex mtx;
} buffer;
std::mutex mtxCon;
std::condition_variable condCon;
bool stop = false;
void produce (int id) {
while (!stop) {
int t = disTime (gen);
int no = disCargo (gen);
std::this_thread::sleep_for (std::chrono::duration<int, std::milli>(1000 * t));
mtxCon.lock();
buffer.mtx.lock();
buffer.buf.push (no);
buffer.mtx.unlock();
mtxCon.unlock();
condCon.notify_one(); // 唤醒一个消费者
printf ("producer id %d generates cargo %d after %d seconds\n", id, t, no);
}
printf("producer %id stops............\n");
}
void consume (int id) {
while (!stop) {
int no = 0;
{
std::unique_lock<std::mutex> lock (buffer.mtx);
condCon.wait (lock, [&buffer]() {
return !buffer.buf.empty();
});
no = buffer.buf.front();
}
buffer.buf.pop();
int t = disTime(gen);
printf("consumer id %d gets cargo %d and will sleep for %d seconds\n", id, no, t);
std::this_thread::sleep_for (std::chrono::duration<int, std::milli>(1000 * t));
}
printf("consumer %id stops............\n");
}
void handleSignal(int sig) {
if (sig != SIGINT) {
return;
}
puts("get signal SIGINT.........................");
stop = true;
}
int main() {
struct sigaction sa;
bzero(&sa, sizeof(sa));
sa.sa_handler = handleSignal;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGINT, &sa, nullptr) == -1) {
perror("sigaction error\n");
return 1;
}
for (int i = 0; i < 10; ++i) {
producerThreads.emplace_back(std::thread(&produce, i));
consumerThreads.emplace_back(std::thread(&consume, i));
}
for (auto &th: producerThreads) {
th.join();
}
for (auto &th: consumerThreads) {
th.join();
}
return 0;
}