概念
多线程(英文:multithreading)多线程程序包含了可以并发运行的两个或更多个程序部分。这样程序中的每个部分称为一个线程,并且每个线程都定义了一个单独的执行路径
std::thread类
官方文档
1. 最简单的线程
void foo()
{
// do something...
}
thread t1(foo);
t1.join();
thread t2(foo);
t2.join();
t1.join() 阻塞当前线程,直到t1线程执行完成才执行t2线程
2. detach 容许线程从线程句柄独立开来执行
void foo()
{
// do something...
}
thread t1(foo);
t1.detach();
thread t2(foo);
t2.detach();
从 thread 对象分离执行的线程,允许执行独立地持续。一旦线程退出,则释放所有分配的资源。调用 detach 后, t1 不再占有任何线程。
3. std::bind()
利用std::bind()表达式绑定对象和其非静态成员函数
class Ttest
{
public:
Ttest(){}
~Ttest(){}
void Hello(int a)
{
printf("thread4 hello world. int a = %d\n", a);
}
private:
};
Ttest test;
thread t4(std::bind(&Ttest::Hello, &test, 100));
printf("t4.joinable = %d\n", t4.joinable());
t4.join();
- Lambda表达式
thread t3([](int a, int b) {
printf("thread3 in a=%d, int b =%d\n", a, b);
}, 20, 22);
t3.join();
std::future类模板
类模板std::future提供访问异步操作结果的机制。(通过 std::async 、 std::packaged_task 或 std::promise 创建的)异步操作能提供一个 std::future 对象给该异步操作的创建者。
1. std::async
int f(int x, int y)
{
return std::pow(x, y);
}
future<int> f1 = async(launch::async, f, 100, 2);
int result1 = f1.get();
printf("result1=%d\n", result1);
- std::packaged_task
int f(int x, int y)
{
return std::pow(x, y);
}
packaged_task<int(int, int)> task(f);
future<int> result = task.get_future();
thread tf(move(task), 2, 10);
tf.join();
printf("ft(f) : %d\n", result.get());
- std::promise
int f(int x, int y)
{
return pow(x, y);
}
void f_wrapper(promise<int> p, int a, int b)
{
p.set_value(f(a, b));
}
promise<int> p1;
future<int> f11 = p1.get_future();
thread tp1(f_wrapper, move(p1), 100, 2);
tp1.join();
int result2 = f11.get();
printf("result2=%d\n", result2);
互斥对象mutex和锁lock
mutex类用于保护共享数据免受从多个线程同时访问。
注意:通常不直接使用 std::mutex : std::unique_lock 、 std::lock_guard 或 std::scoped_lock (C++17 起)以更加异常安全的方式管理锁定。
1. std::lock_guard
class LogFile
{
public:
LogFile()
{
f_.open("log.txt");
}
void shared_print(string s, int value)
{
lock_guard<mutex> guard(mu_);
f_ << "From " << s << " : " << value << endl;
}
private:
mutex mu_;
ofstream f_;
};
void print(LogFile &log)
{
for (int i = 0; i > -100; i--)
{
log.shared_print("print", i);
}
}
LogFile log;
thread t100(print, std::ref(log));
for (int i = 0; i < 100; i++)
log.shared_print("From main", i);
t100.join();
死锁和解决办法
class LogFile
{
public:
LogFile()
{
f_.open("log.txt");
}
void shared_print(string s, int value)
{
lock_guard<mutex> guard(mu_);
lock_guard<mutex> guard2(mu2_);
cout << "From " << s << " : " << value << endl;
}
void shared_print2(string s, int value)
{
lock_guard<mutex> guard2(mu2_);
lock_guard<mutex> guard(mu_);
cout << "From " << s << " : " << value << endl;
}
private:
mutex mu_;
mutex mu2_;
ofstream f_;
};
void print(LogFile &log)
{
for (int i = 0; i > -100; i--)
{
log.shared_print("print", i);
}
}
LogFile log;
thread t100(print, std::ref(log));
for (int i = 0; i < 100; i++)
log.shared_print2("From main", i);
t100.join();
死锁的解决方式1:
- shared_print1和shared_print2函数内,guard1,guard2声明顺序保证相同
void shared_print(string s, int value)
{
lock_guard<mutex> guard(mu_);
lock_guard<mutex> guard2(mu2_);
cout << "From " << s << " : " << value << endl;
}
void shared_print2(string s, int value)
{
lock_guard<mutex> guard(mu_);
lock_guard<mutex> guard2(mu2_);
cout << "From " << s << " : " << value << endl;
}
- std::lock(guard1, guard2)
void shared_print(string s, int value)
{
std::lock(mu_, mu2_);
lock_guard<mutex> guard(mu_, adopt_lock);
lock_guard<mutex> guard2(mu2_, adopt_lock);
cout << "From " << s << " : " << value << endl;
}
void shared_print2(string s, int value)
{
std::lock(mu_, mu2_);
lock_guard<mutex> guard2(mu2_, adopt_lock);
lock_guard<mutex> guard(mu_, adopt_lock);
cout << "From " << s << " : " << value << endl;
}
- std::unique_lock
类 unique_lock 是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用。
- std::defer_lock 捕获的互斥所有权
- std::try_to_lock 尝试获得互斥的所有权而不阻塞
- std::adopt_lock 假设调用方线程已拥有互斥的所有权
class LogFile
{
public:
LogFile()
{
f_.open("log.txt");
}
void shared_print(string s, int value)
{
unique_lock<mutex> lock(mu_, defer_lock);
lock.lock();
cout << "From " << s << " : " << value << endl;
lock.unlock();
}
private:
mutex mu_;
ofstream f_;
};
void print(LogFile &log)
{
for (int i = 0; i > -100; i--)
{
log.shared_print("print", i);
}
}
LogFile log;
thread t100(print, std::ref(log));
for (int i = 0; i < 100; i++)
log.shared_print("From main", i);
t100.join();
注意:
unique_lock拥有更加灵活、丰富的功能,但也非常消耗资源,一般功能简单的情况下请使用lock_guard
std::call_once
准确执行一次可调用 (Callable) 对象 f ,即使同时从多个线程调用。
若在调用 call_once 的时刻, flag 指示已经调用了 f ,则 call_once 立即返回(称这种对 call_once 的调用为消极)。
class LogFile
{
public:
LogFile()
{
}
void shared_print(string s, int value)
{
call_once(flag1_, [&]() {
f_.open("log.txt");
}); // 仅仅调用一次
unique_lock<mutex> lock(mu_, defer_lock);
lock.lock();
cout << "From " << s << " : " << value << endl;
lock.unlock();
}
private:
mutex mu_;
ofstream f_;
once_flag flag1_;
};
std::condition_variable
condition_variable 类是同步原语,能用于阻塞一个线程,或同时阻塞多个线程,直至另一线程修改共享变量(条件)并通知 condition_variable 。
有意修改变量的线程必须:
- 获得 std::mutex (典型地通过 std::unique_lock )
- 在保有锁时进行修改
- 在 std::condition_variable 上执行 notify_one 或 notify_all (不需要为通知保有锁)
mutex mu_;
condition_variable con_;
deque<int> de_;
void function_1()
{
int count = 10;
while (count > 0)
{
unique_lock<mutex> locker(mu_);
de_.push_back(count);
locker.unlock();
con_.notify_one();
std::this_thread::sleep_for(chrono::seconds(1));
count--;
}
}
void function_2()
{
int data = 0;
while (data != 1)
{
unique_lock<mutex> locker(mu_);
con_.wait(locker, []() {return !de_.empty(); });
data = de_.back();
de_.pop_back();
locker.unlock();
cout << "t2 got a value from t1 : " << data << endl;
}
}
thread t101(function_1);
thread t102(function_2);
t101.join();
t102.join();
时间限制函数
// 线程的时间限制
thread t103(f, 1, 2);
this_thread::sleep_for(chrono::milliseconds(3));
chrono::steady_clock::time_point tp = chrono::steady_clock::now()
+ chrono::milliseconds(4);
this_thread::sleep_until(tp);
// 锁的时间限制
mutex mu;
unique_lock<mutex> locker(mu);
locker.try_lock_for(chrono::milliseconds(3));
locker.try_lock_until(tp);
// 条件变量的时间限制
std::condition_variable cond;
cond.wait_for(locker, chrono::milliseconds(3));
cond.wait_until(locker, tp);
// future时间限制
std::promise<int> p;
std::future<int> f = p.get_future();
f.wait_for(chrono::milliseconds(3));
f.wait_until(tp);
八种方式创建子线程
class A
{
public:
void f(int x, int y) {}
int operator()(int n) { return 0; }
};
void foo(int a)
{
}
A a;
thread t1(a, 6); // 传递a的拷贝给子线程
thread t2(ref(a), 6); // 传递a的引用给子线程
thread t3(move(a), 6); // a在主线程中将不再有效
thread t4(A(), 6); // 传递临时创建的a对象给子线程
thread t5(foo, 6);
thread t6([](int x) {return x * x; }, 6);
thread t7(&A::f, a, 8, 'w'); // 传递a的拷贝的成员函数给子线程
thread t8(&A::f, &a, 8, 'w'); // 传递a的地址的成员函数给子线程