C++98多线程依赖于操作系统的多线程API,C++11添加了编程语言线程库。本文只介绍API,默认读者懂OS相关概念。
基本语法
获取线程id
this_thread::get_id()
join detach
join指等待子线程执行完
#include "iostream"
#include "thread"
using namespace std;
void myprint() {
printf("hello world\n");
}
int main() {
thread mythread(myprint);
mythread.join();
}
detach将主线程和子线程分离,分别执行。
正常来说有子线程运行时应该让主线程一直运行,子线程释放后再由主线程进行一些收尾工作。若使用detach方法可以将主线程和子线程分离,使子线程进入后台运行。一旦detach后不能再join。子线程会进入系统后台。直到进程退出。会把很多简单的问题搞得很复杂容易产生异常,不建议使用。
thread不仅可以使用函数实例化,还可以使用匿名函数、可调用对象实例化(operator ())。
使用匿名函数、operator()初始化
#include "bits/stdc++.h"
using namespace std;
int main() {
auto printer = [] {
int count = 0;
time_t start = ::clock();
while (::clock() - start < 1000) {
cout << count++ << endl;
}
};
thread printThread(printer);
printThread.join();
}
#include "bits/stdc++.h"
using namespace std;
struct Print {
void operator()() {
time_t start = clock();
int count = 0;
while (clock() - start < 1000) {
cout << "sub thread print\t" << count++ << endl;
}
}
};
int main() {
Print printer;
thread printThread(printer);
printThread.join();
}
参数传递
使用临时构造对象
join随意怎么用都没事。如果用detach就不要使用指针,要用临时构造对象,避免隐式转换才能保证主线程执行完之前把参数传递给子线程。使用临时构造对象时临时对象的构造和往子线程的拷贝构造都是在主线程进行的,否则会在子线程中进行(当主线程释放时变量也会消失,子线程无法完成构造)。
thread自己会进行一次拷贝构造。要在子线程中使用引用来接:使用引用会构造两次,不用引用会构造三次。即使使用引用,主线程内存中的变量也不会改变。const是语法要求,使用引用就必须加一个const。要想修改成员变量就要声明该变量为mutable,很不方便。
#include "bits/stdc++.h"
using namespace std;
struct A {
mutable int a;
explicit A(int _a) {
this->a = _a;
printf("main thread constructor\n");
cout << this << endl;
cout << string(60, '_') << endl;
}
A(const A& a) {
printf("copy constructor\n");
this->a = a.a;
cout << string(60, '_') << endl;
}
};
void changeValue(const A& a) {
a.a = 12;
}
int main() {
A a(3);
cout << "&a = " << &a << endl;
thread mythread(changeValue, a);
mythread.join();
cout << a.a << endl;
}
out:
main thread constructor
0x7ffe4db7f36c
____________________________________________________________
&a = 0x7ffe4db7f36c
copy constructor
____________________________________________________________
3
Process finished with exit code 0
使用引用传递
如果想要修改成员便令的值要使用std::ref函数。
#include "bits/stdc++.h"
using namespace std;
struct A {
int a;
explicit A(int _a) {
this->a = _a;
printf("main thread constructor\n");
cout << this << endl;
cout << string(60, '_') << endl;
}
A(const A& a) {
printf("copy constructor\n");
this->a = a.a;
cout << string(60, '_') << endl;
}
};
void changeValue(A& a) {
a.a = 12;
}
int main() {
A a(3);
cout << "&a = " << &a << endl;
thread mythread(changeValue, ref(a));
mythread.join();
cout << a.a << endl;
}
out:
main thread constructor
0x7ffe55056af4
____________________________________________________________
&a = 0x7ffe55056af4
12
使用类函数
类函数要加引用。此时对象不需要写ref(),写&即可。
#include <iostream>
#include <thread>
class MyClass {
public:
void MyFunction() {
std::cout << "My Function" << std::endl;
}
};
int main() {
MyClass myObject;
std::thread myThread(&MyClass::MyFunction, &myObject);
myThread.join();
return 0;
}
mutex
引入头头文件
#include<mutex>
lock, unlock
mutex my_mutex;
my_mutex.lock();
// 操作
my_mutex.unlock();
例1 消费者生产者问题
#include "bits/stdc++.h"
using namespace std;
struct ProduceQueue {
deque<int> q;
mutex my_mutex;
void produceAnItem(int i) {
my_mutex.lock();
q.push_back(i);
my_mutex.unlock();
cout << "+1" << endl;
}
bool consumeAnItem() {
if (q.empty()) return false;
my_mutex.lock();
q.pop_front();
cout << "-1" << endl;
my_mutex.unlock();
return true;
}
};
void consumeLoop(ProduceQueue &pq) {
for (int i = 0; i < 1000; i += 0) {
if (pq.consumeAnItem()) i++;
}
cout << "消费1000个产品 over" << endl;
}
void produceLoop(ProduceQueue &pq) {
for (int i = 0; i < 1000; i++) {
pq.produceAnItem(i);
}
cout << "生产1000个产品 over" << endl;
}
int main() {
ProduceQueue pq;
thread consumer(consumeLoop, ref(pq));
thread producer(produceLoop, ref(pq));
producer.join();
consumer.join();
}
lock_guard
语法是:
std::mutex my_mutex;
std::lock_guard<std::mutex> mutex_guard(my_mutex);
得到的mutex_guard相当于一个类对象。可以理解为它的构造函数是执行
my_mutex.lock()
析构函数是执行
my_mutex.unlock()
将例1中的代码块做如下更改即可。
//origin:
my_mutex.lock();
q.pop_front();
my_mutex.unlock();
//changed:使用{}限制范围
{
lock_guard<mutex> lg(my_mutex);
q.pop_front();
}