赞
踩
void funa() { cout << "funa()" << endl; } void funb(int a) { cout << "funb(int a)" << endl; } void func(int& a) { a += 10; cout << "func(int& a)" << endl; } void fund(int* p) { if (p == nullptr) return; *p += 100; cout << "fund(int* p)" << endl; } int main() { int x = 10; std::thread tha(funa); std::thread thb(funb, x); std::thread thc(func, std::ref(x));//需要引用不能直接给,需要ref告诉其为引用类型 std::thread thd(fund, &x); tha.join(); //等待其他线程结束 thb.join(); thc.join(); thd.join(); cout << x << endl; return 0; }
容许线程从线程句柄独立开来执行
int main()
{
std::thread tha(funa);
tha.detach(); //主线程与开辟线程没有关系,主线程可以首先结束
cout << "main end" << endl;
return 0;
}
主线程结束会将资源剥夺,所有尽量不要进行该操作
int main()
{
std::thread tha(funa);
std::thread thb(std::move(tha));
//std::thread thb(tha); error!!!
//thb = tha;
std::thread thc;
thc = std::move(thb);
thc.join();
return 0;
}
不能通过拷贝构造与赋值语句进行线程之间的转换,只能使用移动构造以及移动赋值进行资源转移
使当前线程的执行停止指定的时间段
int funa(int n,int &x ) { for (int i = 1; i <= n; ++i) { cout << "thread funa" << endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } x = n; cout << "thread funa end" << endl; return n; } //C++无法获得线程的返回值 int main() { int a = 0; //但是可以通过引用的方式进行获取 thread tha(funa, 5, std::ref(a)); tha.join(); cout << "main end" << endl; return 0; }
int funa(int n,int &x ) { for (int i = 1; i <= n; ++i) { cout << "thread funa" << endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } x = n; cout << "thread funa end" << endl; return n; } //C++无法获得线程的返回值 int main() { int a = 0; //但是可以通过引用的方式进行获取 thread tha(funa, 5, std::ref(a)); tha.join(); tha.detach(); cout << "main end" << endl; return 0; }
这样会导致,主线程与子线程分离,子线程根本无法结束
class Object { private: int value; public: Object(int x = 0):value(x){} ~Object() {} void run() { cout << "Object run" << endl; } }; int main() { Object obj; std::thread thobj(&Object::run, &obj); thobj.join(); cout << "main end" << endl; return 0; }
Object::run 线程调用,需要加上&符号,并且传入一个this指针
静态全局函数
class Object { private: int value; public: Object(int x = 0):value(x){} ~Object() {} void run() { cout << "Object run" << endl; } static void fun(int x ) { Object obj(x); obj.run(); cout << "static fun" << endl; } }; int main() { std::thread tha(Object::fun, 5); tha.join(); cout << endl; return 0; }
静态成员函数,线程调用不需要传参this指针,并且可以通过线程调动静态成员的方式去另向调动普通成员函数
检查线程是否可合并,即潜在地运行于平行环境中,也就是线程是否于某一个函数资源进行关联
void foo() { std::this_thread::sleep_for(std::chrono::seconds(1)); cout << "foo end" << endl; } int main() { std::thread t; cout << "befor starting,joinable" << t.joinable() << endl; t = std::thread(foo); //无名对象线程 移动赋值 cout << "after starting,joinable" << t.joinable() << endl; t.join(); cout << "after joining" << t.joinable() << endl; }
int main()
{
cout << t.hardware_concurrency() << endl;//返回CPU内核数
cout << std::thread::hardware_concurrency() << endl;
}
返回CPU当前逻辑处理数
int g_num = 0; void print(int id) { for (int i = 0; i < 5; ++i) { ++g_num; cout << "id:" << id << "==>" << g_num << endl; std::this_thread::sleep_for(std::chrono::seconds(2)); } } int main() { std::thread tha(print, 0); std::thread thb(print, 1); tha.join(); thb.join(); cout << "main end" << endl; }
非原子操作的每次执行并不一定执行结果相同
我们通过原子操作进行定义g_num
std::atomic_int g_num = 0;
可以保证每次结果相同
异变关键字不能解决原子操作的问题
int g_num = 0; std::mutex mtx; void print(int id) { for (int i = 0; i < 5; ++i) { mtx.lock(); ++g_num; cout << "id:" << id << "==>" << g_num << endl; mtx.unlock(); std::this_thread::sleep_for(std::chrono::seconds(1)); } } int main() { std::thread tha(print, 0); std::thread thb(print, 1); tha.join(); thb.join(); cout << "main end" << endl; }
互斥锁只要解决异步操作中对同一个资源的竞争
void print(int id)
{
for (int i = 0; i < 5; ++i)
{
while (!mtx.try_lock()) //试图加锁,加锁失败也会执行
{ //加锁失败则一直循环
cout << "lock..." << endl;
}
++g_num;
cout << "id:" << id << "==>" << g_num << endl;
mtx.unlock();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
try_lock 加锁成功会返回1,失败返回0,倘若加锁失败它依旧会向下执行,所以一般使用会将其于while循环嵌套,则该锁会在无法获得加锁的情况下不断地尝试加锁
递归锁,提供能被同一线程递归锁定的互斥设施
如上图,当我们进入线程,去调动第一个函数进行加锁,随机递归进入第二个线程,继而又进行了加锁,而普通锁进行两次加锁则会出现错误
而当我们使用了递归锁,则在递归过程中再次遇到加锁的时候,则该锁失效直接进入函数,并且每遇到一次锁都会又一次相应的解锁
实现严格基于作用域的互斥体所有权包装器
void print(int id)
{
for (int i = 0; i < 5; ++i)
{
std::lock_guard<std::mutex> lock(mtx); //对象构造 内部调用互斥锁
++g_num;
cout << id << "-->" << g_num << endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
为在作用域块期间占用互斥提供便利RAII风格机制,与智能指针相似
而在上面代码中,作用域块就是for语句,则当for语句结束,该对象进行析构继而就进行解锁
condition_variable 类是同步原语,能用于阻塞一个线程,或同时阻塞多个线程,直至另一线程修改共享变量,并通知condition_variable
std::mutex mx; //互斥量 std::condition_variable cv; //条件变量 int isReady = 0; //全局变量 void print_A() { std::unique_lock<std::mutex> lock(mx); int i = 0; while (i < 10) { while (isReady != 0) { cv.wait(lock);//等待 } cout << "A" << endl; isReady = 1; ++i; std::this_thread::sleep_for(std::chrono::milliseconds(100)); cv.notify_all(); //唤醒所有等待线程 } } void print_B() { std::unique_lock<std::mutex> lock(mx); int i = 0; while (i < 10) { while (isReady != 1) { cv.wait(lock);//等待 } cout << "B" << endl; isReady = 2; ++i; std::this_thread::sleep_for(std::chrono::milliseconds(100)); cv.notify_all(); //唤醒所有等待线程 } } void print_C() { std::unique_lock<std::mutex> lock(mx); int i = 0; while (i < 10) { while (isReady != 2) { cv.wait(lock);//等待 } cout << "C" << endl; isReady = 0; ++i; std::this_thread::sleep_for(std::chrono::milliseconds(100)); cv.notify_all(); //唤醒所有等待线程 } } int main() { thread tha(print_A); thread thb(print_B); thread thc(print_C); tha.join(); thb.join(); thc.join(); }
这样就可以达成,ABC不断循环一个一个打印
当B,C线程进入调用函数,在条件变量不符合情况下,进入wait队列,线程阻塞并且将锁状态释放
直到A线程进入函数,并且由于BC线程在阻塞后将锁状态释放,A线程获得互斥锁,接着判断条件变量符合,不执行等待,进行打印,接着唤醒在等待队列上的所有线程
BC线程被唤醒,C回到被阻塞位置继续执行,并且获得锁,接着由于条件变量不符,又回到了阻塞状态
B线程回到被阻塞位置,获得锁,条件变量符合进行打印,唤醒在等待队列的所有线程
当我们的阻塞队列中拥有较多线程的时候,当我们进行唤醒的时候,将所有阻塞队列的线程都进行唤醒,并且当互斥锁释放后,被唤醒的所有线程都开始抢占这个互斥锁,但是仅仅只有一个线程可以抢占,其他的则又进行等待
notify_one 则是一次仅能唤醒一个线程用来避免惊群现象
void print_A() { std::unique_lock<std::mutex> lock(mx); int i = 0; while (i < 10) { if (isReady != 0) { cv.wait(lock);//等待 } cout << "A" << endl; isReady = 1; ++i; std::this_thread::sleep_for(std::chrono::milliseconds(100)); cv.notify_all(); //唤醒所有等待线程 } }
若我们将原本第二个while修改为if判断,那么假如当A线程唤醒等待线程并且释放锁,BC线程在进入到原先位置继续运行,则会直接进入下一步进行打印,就会导致不能严格按照ABC顺序进行打印
void print_A() { std::unique_lock<std::mutex> lock(mx); int i = 0; while (i < 10) { while (isReady != 0) { cv.wait(lock);//等待 } cout << "A" << endl; isReady = 1; ++i; std::this_thread::sleep_for(std::chrono::milliseconds(100)); cv.notify_one(); } }
若我们将原先唤醒所有线程修改为仅仅唤醒一个线程,那么会导致若我们需要下一次执行B线程,而C线程先抢占到了互斥锁,那么会导致C再次回到等待队列,C线程无法去执行唤醒操作,导致所有线程都进入到了阻塞队列
std::mutx mx;
std::condition_variable cv;
int number = 1;
void Print_A()
{
unique_lock<std::mutex> lock(mx);
unique_lock<std::mutex> lc(std::move(lock));
}
可以将锁的拥有权进行转移
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。