贡献者: addis
std::thread
底层通常使用 pthread。
#include <thread>
,#include <mutex>
std::thread th(函数, arg1, arg2, ...)
; 创建一个线程,调用 函数
(可以是函数指针,函数对象,lambda),arg
是 函数
的变量。
th.join()
可以让主程序等待某个线程自己退出。
std::mutex m
可以避免多个线程操作同一数据。该类型不可以 copy 只能通过 reference 传参。
m.lock()
给 mutex 上锁,如果已经被别人上锁就暂停该线程并等待解锁。m.try_lock()
试图上锁,如果已经被别人上锁就返回 false
。
m.unlock()
解锁,以便别的线程上锁。
m.lock()
和 m.unlock()
之间发生 throw(这样就无法自动解锁),通常不直接调用他们,而是用 std::lock_guard<std::mutex> guard(m)
。constructor 相当于 lock,destructor 相当于 unlock。
unique_lock<>
类似于 lock_guard
但更灵活。
std::this_thread::sleep_for(std::chrono::seconds(2));
可以让某个线程暂停
sleep_for
不是 busy wait 而是像 cin
等待时一样运行 cpu 执行别的东西,也就是让系统接管,直到某个事件发生然后再继续。
例程(编译时要给 linker 加上 -l pthread
):
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;
mutex mut;
// A dummy function
void myfun(int id, int *i)
{
if (id == 1)
this_thread::sleep_for(chrono::milliseconds(100));
lock_guard<mutex> guard(mut);
printf("id = %d, i = %d\n", id, *i);
int j = *i+1;
if (id == 2)
this_thread::sleep_for(chrono::milliseconds(100));
*i = j;
}
int main()
{
int i = 0;
thread th1(myfun, 1, &i);
thread th2(myfun, 2, &i);
myfun(0, &i);
th1.join();
th2.join();
return 0;
}
另外也可以把 mut
声明为 myfun()
中的一个 static
变量。
另外注意 mutex
不光适用于 std::thread
,对任何其他 threading 库例如 pthread
或者 OpenMP 都有效。
thread_local
,可以让其在每个线程中保持自己独立的 copy,使函数变得 thread safe。例如上面的 myfun
中如果声明了 thread_local static vector<int> a;
,那么每个线程第一次调用该函数时就会生成一个独立的变量 a
,若一个线程多次调用该函数,那么每次调用时 a
都会有上次调用结束前的值。
condition_variable
用于 block 一个或者多个线程(也就是让它睡觉,腾出算力做别的事情),直到某个另外的线程发送一个信号。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
// 把 condition_variable 需要 bool 变量一起封装起来更方便使用
class ConditionVar {
private:
std::condition_variable cv;
bool ready = false;
public:
// 暂停一些线程
void wait(std::mutex &mtx) {
std::unique_lock<std::mutex> lock(mtx); // 上锁
cv.wait(lock, [this]{ return ready; }); // 在线程休眠以前解锁,在唤醒时再次上锁
// 注意如果在 wait 以前 ready=true,那么 wait 什么都不做。
// 解锁
}
// 唤醒所有线程
void notify_all(std::mutex &mtx) {
std::lock_guard<std::mutex> lock(mtx);
ready = true;
cv.notify_all();
}
// 随机唤醒一个线程
void notify_one(std::mutex &mtx) {
std::lock_guard<std::mutex> lock(mtx);
ready = true;
cv.notify_one();
}
};
std::mutex mtx;
ConditionVar cv;
void wait(int id) {
printf("Thread %d started.\n", id);
cv.wait(mtx); // 休眠
printf("Thread %d is resumed.\n", id);
}
void resume() {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟一些工作
cv.notify_all(mtx); // 全部唤醒
printf("resume signal sent!\n");
}
int main() {
std::thread threads[3];
// 注意这里任何一个线程都可能先开启
for (int i = 0; i < 2; ++i)
threads[i] = std::thread(wait, i);
threads[2] = std::thread(resume);
for (auto &th : threads)
th.join();
}
运行结果
Thread 0 started.
Thread 1 started.
resume signal sent!
Thread 0 is resumed.
Thread 1 is resumed.
这有什么应用呢?比如说一个线程要准备一个全局的 string 变量给所有其他线程用。那么所有其他线程在使用该 string 之前就要 wait,而准备 string 的线程准备好之后就要 notify_all。
c++ 没有定义自旋锁,我们可以简单实现一个:
#include <atomic>
#include <thread>
#include <iostream>
class SpinLock {
private:
std::atomic_flag lockFlag = ATOMIC_FLAG_INIT;
public:
void lock() {
while (lockFlag.test_and_set(std::memory_order_acquire)) {
// Spin-wait (busy wait) until the lock is released
}
}
void unlock() {
lockFlag.clear(std::memory_order_release);
}
};
void exampleFunction(SpinLock& spinLock) {
spinLock.lock();
std::cout << 123 << "234" << std::endl;
spinLock.unlock();
}
int main() {
SpinLock spinLock;
std::thread t1(exampleFunction, std::ref(spinLock));
std::thread t2(exampleFunction, std::ref(spinLock));
t1.join();
t2.join();
return 0;
}
 
 
 
 
 
 
 
 
 
 
 
友情链接: 超理论坛 | ©小时科技 保留一切权利