入门
#include <iostream>
#include <thread>
void hello() //线程的起始函数
{
std::cout << "Hello" << std::endl;
}
int main()
{
std::thread t(hello); //子线程的起始函数在thread的构造函数中指明
t.join(); //等待子线程结束
return 0;
}
编译指令:
g++ -pthread test.cpp
任何可调用类型都适用与std::thread
future等待一次性事件发生
future与async的使用
示例1:
int find_answer() {
return 123;
}
void do_other_thing() {
this_thread::sleep_for(chrono::seconds(3));
}
int main()
{
future<int> ans = async(find_answer);
do_other_thing();
cout << ans.get() << endl;
return 0;
}
示例2:
class X
{
public:
int exe(int& a)
{
a += 10;
return a;
}
};
void do_other_thing()
{
this_thread::sleep_for(chrono::seconds(1));
}
int main()
{
X x;
int a = 1;
future<int> ans = async(launch::deferred, &X::exe, &x, ref(a)); // 在当前线程上延后调用任务函数,不会开启其他线程
do_other_thing();
ans.wait();
return 0;
}
future与packaged_task的使用
示例1:
int add(int a, int b)
{
return a + b;
}
int main()
{
packaged_task<int(int, int)> task(add);
future<int> res = task.get_future();
thread t(move(task), 10, 20);
cout << res.get() << endl;;
t.join();
return 0;
}
示例2:
int bKeepThread; //是否维持线程
int bPostTaskToThread; //是否发送任务给线程,并指定任务
mutex mtx;
deque<packaged_task<void()>> tasks_dq; //任务队列
void Thread() //用于执行任务的线程
{
while(bKeepThread) {
packaged_task<void()> task;
{
lock_guard<mutex> lg(mtx);
if (tasks_dq.empty()) {
continue;
}
task = move(tasks_dq.front());
tasks_dq.pop_front();
}
task();
}
}
void post_task_to_thread(void f()) //发送任务
{
packaged_task<void()> task(f);
lock_guard<mutex> lg(mtx);
tasks_dq.push_back(move(task));
cout << tasks_dq.size() << endl;
}
void func_1()
{
cout << "func_1" << endl;
}
void func_2()
{
cout << "func_2" << endl;
}
void func_3()
{
cout << "func_3" << endl;
}
int main()
{
bKeepThread = 1; //初始时,先维持线程
bPostTaskToThread = 1; //初始时,先发送func_1给线程
thread t1(Thread); //开启线程
typedef void (*Func)();
Func arr_f[3] = {&func_1, &func_2, &func_3};
cout << "Keep Thread and post a task to it?:" << endl;
while (bKeepThread) {
if (bPostTaskToThread >= 1 && bPostTaskToThread <= 3) {
post_task_to_thread(arr_f[bPostTaskToThread - 1]); //发送任务
}
cout << "Keep Thread and post a task to it?:" << endl;
cin >> bKeepThread >> bPostTaskToThread;
}
t1.join();
cout << "end" << endl;
return 0;
}
执行:
$ g++ -pthread test.cpp
$./a.out
Keep Thread and post a task to it?:
1
Keep Thread and post a task to it?:
func_1
1 1
1
Keep Thread and post a task to it?:
func_1
1 2
1
Keep Thread and post a task to it?:
func_2
1 3
1
Keep Thread and post a task to it?:
func_3
0 1
end
future与promise的使用
void setValue(promise<int>& prms)
{
prms.set_value(123); //设置future将会获取的值
}
int main()
{
promise<int> prms;
future<int> ft = prms.get_future(); //future与promise关联
thread t(setValue, ref(prms));
int res = ft.get();
t.join();
cout << res << endl;
return 0;
}
future抛出异常
示例1:异步async
struct Err
{
char str[10] = "Err";
};
void func()
{
throw Err();
}
int main()
{
future<void> ft = async(func);
try {
ft.get();
}
catch(const Err& err) {
cout << err.str << endl;
}
return 0;
}
示例2:packaged_task
struct Err
{
char str[10] = "Err";
};
void func()
{
throw Err();
}
int main()
{
packaged_task<void()> task(func);
future<void> ft = task.get_future();
thread t(move(task));
t.detach();
try {
ft.get();
}
catch(const Err& err) {
cout << err.str << endl;
}
return 0;
}
示例3:promise
struct Err
{
char str[10] = "Err";
};
int main()
{
promise<void> prms;
future<void> ft = prms.get_future();
thread t([&prms](){
prms.set_exception(make_exception_ptr(Err()));
});
t.detach();
try {
ft.get();
}
catch(const Err& err) {
cout << err.str << endl;
}
return 0;
}
多个线程一起等待
future会独占异步结果,get只能被一个线程调用,因为get会进行移动操作,之后指就不存在了。
shared_future的实例能复制副本,可以持有该类的多个对象,指向同一个异步任务的状态数据。
每个线程复制一个shared_future的副本,为各自线程独自持有,作为各线程内部数据,由标准库正确同步,可以安全访问。
int main()
{
promise<void> ready_promise, t1_ready_promise, t2_ready_promise;
shared_future<void> ready_future(ready_promise.get_future());
chrono::time_point<chrono::high_resolution_clock> start;
auto fun1 = [&, ready_future]() -> chrono::duration<double, std::milli>
{
t1_ready_promise.set_value();
ready_future.wait(); // 等待来自 main() 的信号
return chrono::high_resolution_clock::now() - start;
};
auto fun2 = [&, ready_future]() -> chrono::duration<double, std::milli>
{
t2_ready_promise.set_value();
ready_future.wait(); // 等待来自 main() 的信号
return chrono::high_resolution_clock::now() - start;
};
auto result1 = async(launch::async, fun1);
auto result2 = async(launch::async, fun2);
// 等待线程变为就绪
t1_ready_promise.get_future().wait();
t2_ready_promise.get_future().wait();
// 线程已就绪,开始时钟
start = chrono::high_resolution_clock::now();
// 向线程发信使之运行
ready_promise.set_value();
cout << "Thread 1 received the signal "
<< result1.get().count() << " ms after start\n"
<< "Thread 2 received the signal "
<< result2.get().count() << " ms after start\n";
return 0;
}
限时等待
示例:
condition_variable cv;
mutex cv_m;
atomic<int> i{0};
void waits(int idx)
{
unique_lock<std::mutex> lk(cv_m);
auto now = chrono::system_clock::now();
if(cv.wait_until(lk, now + idx * 100ms, [](){return i == 1;}))
cerr << "Thread " << idx << " true i == " << i << '\n';
else
cerr << "Thread " << idx << " false i == " << i << '\n';
}
void signals()
{
this_thread::sleep_for(120ms);
cerr << "Notifying...\n";
cv.notify_all();
std::this_thread::sleep_for(100ms);
i = 1;
cerr << "Notifying again...\n";
cv.notify_all();
}
int main()
{
std::thread t1(waits, 1), t2(waits, 2), t3(waits, 3), t4(signals);
t1.join();
t2.join();
t3.join();
t4.join();
}
执行结果:
$ g++ -pthread test.cpp
$ ./a.out
Thread 1 false i == 0
Notifying...
Thread 2 false i == 0
Notifying again...
Thread 3 true i == 1
解释:
线程1,2,3分别等待100,200,300ms。在signal线程进入sleep时,线程1等待时间结束,检查i是否为1,结果为false,wait_until返回false;signal线程等待结束后唤醒线程2和3,线程2和3分别检查i是否为1,结果为否,只能释放锁继续进入等待;线程2等待时间结束,检查i是否为1,结果为false,wait_until返回false;signal线程又一次等待结束后,设i为1,唤醒线程3,线程3检查i是否为1,结果为true,wait_until返回true。
这里wait_until包含3个参数,如果时间点未到达就被唤醒,会根据条件是否满足判断向下执行还是继续等待,如果到达了时间点,返回条件结果,直接向下执行。
原子定义
atomic<int> counter(0);
void incrementCounter()
{
for (int i = 0; i < 10000; i++) {
counter.fetch_add(1, memory_order_relaxed);
}
}
int main()
{
thread t1(incrementCounter);
thread t2(incrementCounter);
t1.join();
t2.join();
cout << "Counter value: " << counter << endl;
return 0;
}