关于多线程,一直都是一知半解,今天又看了些相关知识,总结下:
1.过去写的单片机裸跑的程序,其实也属于多线程的,用智能电表中的固件做个比方。
void main(void){
initilize_para(); //基础参数的初始化
.....
while
{
DealWith485Com(); //485通信
DealWithEnergy(); //电量数据处理
DealWithIRCOM(); //红外通信任务
...... //其他任务
}
}
以上,在while{}循环中,所列包括3个“线程”处理,线程1在运行完之后运行线程2,线程2运行完进入线程3....,后面依次,此类我觉得也可称之“线程”。由于不必存在同时处理的问题,因此不存在争抢同一共享区域的问题。
2.通常意义上的多线程,通过时间片分配各个线程的运行时间,表面上看似可以同时运行。通过creat_thread()方式创建线程,线程运行完毕则退出。存在不同线程争抢共享资源的问题。以C++11实现简单生产者消费者模式说明。
#include "stdafx.h"
#include <thread>
#include <mutex>
#include <deque>
#include <vector>
#include <condition_variable>
class CThreadDemo
{
private:
std::deque<int> m_data;
std::mutex m_mtx; // 全局互斥锁.
std::condition_variable m_cv; // 全局条件变量.
int m_nGen;
private:
void ProductThread(){
printf("PTr");
while (true){
std::unique_lock <std::mutex> lck(m_mtx);
m_nGen = ++m_nGen % 1000;
printf("product %d\n", m_nGen);
m_data.push_back(m_nGen);
lck.unlock();
m_cv.notify_all();
/* 等待1S */
std::chrono::milliseconds dura(1000);
std::this_thread::sleep_for(dura);
}
}
void ConsumeThread(){
printf("CTr");
while (true){
std::unique_lock <std::mutex> lck(m_mtx);
while (m_data.empty()){
m_cv.wait(lck);
}
int nData = m_data.front();
m_data.pop_front();
printf("consume %d\n", nData);
lck.unlock();
/* 等待2S */
std::chrono::milliseconds dura(2000);
std::this_thread::sleep_for(dura);
}
}
public:
CThreadDemo(){
m_data.clear();
m_nGen = 0;
}
void Start(){
std::vector<std::thread> threads;
threads.clear();
for (int i = 0; i < 5; i++){/* 生产者线程 */
threads.push_back(std::thread(&CThreadDemo::ProductThread, this));
}
for (int i = 5; i < 10; i++){/* 消费者线程 */
threads.push_back(std::thread(&CThreadDemo::ConsumeThread, this));
}
for (auto& t : threads){/* 等待所有线程的退出 */
t.join();
}
}
};
int _tmain(int argc, _TCHAR* argv[])
{
CThreadDemo test;
test.Start();
return 0;
}
实现了一个简单的生产者-消费者模式,生产者每1S将一个数值放到双向队列中,消费者每2S从双向队列中取出数据。主要介绍下std::condition_variable的使用。
<condition_variable>是C++标准程序库中的一个头文件,定义了C++11标准中的一些用于并发编程时表示条件变量的类与方法等。
条件变量是并发程序设计中的一种控制结构。多个线程访问一个共享资源(或称临界区)时,不但需要用互斥锁实现独享访问以避免并发错误(称为竞争危害),在获得互斥锁进入临界区后还需要检验特定条件是否成立:
(1)、如果不满足该条件,拥有互斥锁的线程应该释放该互斥锁,把自身阻塞(block)并挂到(suspend)条件变量的线程队列中
(2)、如果满足该条件,拥有互斥锁的线程在临界区内访问共享资源,在退出临界区时通知(notify)在条件变量的线程队列中处于阻塞状态的线程,被通知的线程必须重新申请对该互斥锁加锁。
std::condition_variable类的成员函数:
(1)、构造函数:仅支持默认构造函数,拷贝、赋值和移动(move)均是被禁用的。
(2)、wait:当前线程调用wait()后将被阻塞,直到另外某个线程调用notify_*唤醒当前线程;当线程被阻塞时,该函数会自动调用std::mutex的unlock()释放锁,使得其它被阻塞在锁竞争上的线程得以继续执行。一旦当前线程获得通知(notify,通常是另外某个线程调用notify_*唤醒了当前线程),wait()函数也是自动调用std::mutex的lock()。wait分为无条件被阻塞和带条件的被阻塞两种。
无条件被阻塞:调用该函数前,当前线程应该已经对unique_lock<mutex> lck完成了加锁。所有使用同一个条件变量的线程必须在wait函数中使用同一个unique_lock<mutex>。该wait函数内部会自动调用lck.unlock()对互斥锁解锁,使得其他被阻塞在互斥锁上的线程恢复执行。使用本函数被阻塞的当前线程在获得通知(notified,通过别的线程调用 notify_*系列的函数)而被唤醒后,wait()函数恢复执行并自动调用lck.lock()对互斥锁加锁。
带条件的被阻塞:wait函数设置了谓词(Predicate),只有当pred条件为false时调用该wait函数才会阻塞当前线程,并且在收到其它线程的通知后只有当pred为true时才会被解除阻塞。因此,等效于while (!pred()) wait(lck).