在C++环境下面, 要实现多线程并发, 提升性能, 并不是一件很容易的事情。 难点不是如何划分出多个线程, 而是如何确认哪些内存是线程间公用的, 需要避免冲突, 另一个方面, 如何做到线程间同步。
线程私有数据
在一个线程里面, 线程ID和堆栈上的数据, 以及信号集等是线程私有的, 我们还可以通过一些函数指定线程的私有数据, 比如pthread_setspecific。
线程之间通信
线程之间的通信方式, 常见的有2种:信号通知以及管道通知。
信号通知
通常会有信号的产生端和消费端, 信号的产生端通常是有一个队列, 每当有新的信号产生, 就加入到信号里面, 消费端通常是一个后台线程, 每当收到通知, 信号队列非空, 就会暂时锁定队列, 并且将队列的内容swap到新的队列, 解锁之后, 就可以逐个遍历,处理每个信号。
信号产生端:
class Producer {
std::list<Item*> queue;
void AddItem(Item* item) {
pthread_lock(&mutex);
queue.push_back(item);
pthread_cond_signal(&cond);
pthread_unloc(&mutex);
}
}
信号消费端:
class Consumer {
public:
void process() {
pthread_mutex_lock();
while (1) {
std::list<Item*> queuetmp;
if (!queue.empty()) {
queuetmp.swap(queue);
}
pthread_mutex_unlock();
while (!queuetmp.empty()) {
Item* item = queuetmp.front();
queuetmp.pop_front();
handler(item);
}
pthread_mutex_lock();
pthread_cond_wait(&cond);
}
}
}
管道通知
管道是使用非常广泛的一种消息通知机制, 可以用于线程间或者进程间通信。pipe函数会产生2个文件描述符,通过pipe函数创建的这两个文件描述符 fd[0] 和 fd[1] 分别构成管道的两端,往 fd[1] 写入的数据可以从 fd[0] 读出。并且 fd[1] 一端只能进行写操作,fd[0] 一端只能进行读操作,不能反过来使用。要实现双向数据传输,可以使用两个管道。
信号产生端:
class Producer {
std::list<Item*> queue;
int wrfd;
void AddItem(Item* item) {
pthread_lock(&mutex);
queue.push_back(item);
pthread_unloc(&mutex);
// write message to pipe to notify consumer part
write(wrfd, data, size);
}
}
信号消费端:
class Consumer {
private:
int rdfd;
public:
void process() {
// pipe data reader part, using read blocking-mode,
// only when producer part has data written, it will go to
// the while range
while (read(rdfd, buf, size) > 0) {
pthread_mutex_lock();
std::list<Item*> queuetmp;
if (!queue.empty()) {
queuetmp.swap(queue);
}
pthread_mutex_unlock();
while (!queuetmp.empty()) {
Item* item = queuetmp.front();
queuetmp.pop_front();
handler(item);
}
}
}
}