前言
对于多线程,之前几章内容已经详细讲解了Linux环境下如何创建多线程,本章内容主要讲解Windows环境下多线程的创建和使用,对于多线程的基本概念在这里不做过多的补充。
一、thread
#include< thread >
由于在windows中我们不再能使用pthread库,因为pthread是unix系统的原生系统库,所以,我们需要包 #include< thread > 头文件。
该头文件包含了thread类及其相关函数,并且不再像pthread库一样通过调用系统接口函数来创建多线程,而是通过对象来帮我们创建多线程。
以我们之前写的抢票多线程程序示例
示例代码如下
#include<iostream>
#include<thread>
#include<string>
#include<mutex>
#include<condition_variable>
int tickets = 10000;
std::mutex mutex;
void task(std::string name)
{
while (1)
{
mutex.lock();
if (tickets)
{
tickets--;
std::cout << name << " : " << tickets << std::endl;
}
mutex.unlock();
}
}
int main()
{
std::thread t1(task,"thread 1");
std::thread t2(task, "thread 2");
std::thread t3(task, "thread 3");
t1.join();
t2.join();
t3.join();
return 0;
}
这里的thread对象的初始化可以是无参构造,而另一种构造的参数fn是你的新线程要运行的函数,args则是参数包。
mutex
提供了lock_guard和unique_lock来对我们的互斥锁进行了一种保护。
lock_guard的原理则是
#include<iostream>
#include<thread>
#include<string>
#include<mutex>
#include<condition_variable>
class lockGuard
{
public:
lockGuard(std::mutex& mutex)
:_mutex(mutex)
{
_mutex.lock();
}
~lockGuard()
{
_mutex.unlock();
}
private:
std::mutex& _mutex;
};
int tickets = 10000;
std::mutex mutex;
void task(std::string name)
{
while (1)
{
lockGuard lock(mutex);
if (tickets)
{
tickets--;
std::cout << name << " : " << tickets << std::endl;
}
else
{
break;
}
}
}
int main()
{
std::thread t1(task,"thread 1");
std::thread t2(task, "thread 2");
std::thread t3(task, "thread 3");
t1.join();
t2.join();
t3.join();
return 0;
}
而unique_lock和lock_guard不同之处就在于unique_lock可以根据工作任务需求临时解锁再上锁。
二、奇偶双线程打印
上面的代码我们可以知道还缺少了条件变量来控制我们的多线程,我们再来看另外一种示例代码,然后大家如果有时间自行再对上面的抢票程序进行优化。
#include<condition_variable>
条件变量的使用也变成了通过调用对象的成员函数来使用。
对象名.wait() 类似于使用pthread_cond_wait(mutex) .
对象名.notify_one() 类似于使用pthread_cond_signal(mutex).
对象名.notify_all() 类似于使用pthread_cond_broadcast(mutex).
其中wait()需要unique_lock类型才能使用。
示例代码如下
#include<iostream>
#include<thread>
#include<string>
#include<mutex>
#include<condition_variable>
int main()
{
//奇偶双线程
std::thread threads[2];
int n = 1000;
std::mutex mtx;
std::condition_variable cv;
threads[0] = std::thread([&]() {
while (1)
{
//std::lock_guard<std::mutex>lock(mtx); //RAII
std::unique_lock<std::mutex>lock(mtx);
if (n % 2 != 0) //偶数则线程1打印
{
cv.wait(lock); //奇数则阻塞
}
if (n > -1)
{
std::cout << "thread 1" << " : " << n << std::endl;
n--;
cv.notify_one();
}
else
{
cv.notify_one();
break;
}
}
} );
threads[1] = std::thread([&]() {
while (1)
{
//std::lock_guard<std::mutex>lock(mtx);
std::unique_lock<std::mutex>lock(mtx);
if (n % 2 == 0) //奇数则线程2打印
{
cv.wait(lock); //偶数则阻塞
}
if (n > -1)
{
std::cout << "thread 2" << " : " << n << std::endl;
n--;
cv.notify_one();
}
else
{
cv.notify_one();
break;
}
}
});
threads[0].join();
threads[1].join();
return 0;
}
在上面的示例代码需要注意的是 我们使用了thread的移动拷贝构造和lambda表达式来初始化。