
- thread
- 1. 构造函数
- 2. join()
- 3. detach()
- 4. joinable()
- 多线程下的全局变量
- 1. 加入互斥量std::metux
- 2. 条件变量
定义在头文件#include
类 thread 表示单个执行线程。线程允许多个函数同时执行。
std::thread 对象也可能处于不表示任何线程的状态(默认构造、被移动、 detach 或 join 后),并且执行线程可能与任何 thread 对象无关( detach 后)。
没有两个 std::thread 对象会表示同一执行线程; std::thread 不是可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 的,尽管它可移动构造 (MoveConstructible) 且可移动赋值 (MoveAssignable) 。
1. 构造函数
thread() noexcept; //(1) (C++11 起)
thread( thread&& other ) noexcept; //(2) (C++11 起)
template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args ); //(3) (C++11 起)
thread( const thread& ) = delete; // (4) (C++11 起)
构造新的 thread 对象。
-
构造不表示线程的新 thread 对象。
-
移动构造函数。构造表示曾为 other 所表示的执行线程的 thread 对象。此调用后 other 不再表示执行线程。
-
构造新的 std::thread 对象并将它与执行线程关联。新的执行线程开始执行 。
示例:构造四个线程,挂接四个函数
void funa()
{
cout << "funa()" << endl;
}
void funb(int a)
{
a += 1;
cout << "funb(int a)" << endl;
}
void func(int* a)
{
*a += 10;
cout << "func(int* a)" << endl;
}
void fund(int& a)
{
a += 20;
cout << "fund(int& a)" << endl;
}
int main(void)
{
int x = 10;
std::thread tha(funa);
std::thread thb(funb, x);
std::thread thc(func, &x);
std::thread thd(fund, std::ref(x));
cout << "main end" << endl;
return 0;
}
这样运行是会报错的,因为主函数提前结束,而构造的线程还没执行完。
2. join()所以需要引入join()方法,意思是等待线程完成其 *** 作:
void join();
阻塞当前线程直至 *this 所标识的线程结束其执行。
*this 所标识的线程的完成同步于对应的从 join() 成功返回。
*this 自身上不进行同步。同时从多个线程在同一 thread 对象上调用 join() 构成数据竞争,导致未定义行为。
主函数加入等待函数后,就可以正常执行。
int main(void)
{
int x = 10;
std::thread tha(funa);
std::thread thb(funb, x);
std::thread thc(func, &x);
std::thread thd(fund, std::ref(x));
tha.join();
thb.join();
thc.join();
thd.join();
cout << "main end" << endl;
return 0;
}
3. detach()
容许线程从线程句柄独立开来执行
void detach();
从 thread 对象分离执行线程,允许执行独立地持续。一旦该线程退出,则释放任何分配的资源。
调用 detach 后 *this 不再占有任何线程。
检查线程是否可合并,即潜在地运行于平行环境中 .
bool joinable() const noexcept;
检查 std::thread 对象是否标识活跃的执行线程。具体而言,若 get_id() != std::thread::id() 则返回 true 。故默认构造的 thread 不可结合。
结束执行代码,但仍未结合的线程仍被当作活跃的执行线程,从而可结合。
示例:
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
cout << "foo end" << endl;
}
int main(void)
{
std::thread tha;
cout << "Before starting , joinable: " << tha.joinable() << endl;
tha = std::thread(foo);
cout << "After starting, joinable: " << tha.joinable() << endl;
tha.join();
cout << "After joining, joinable: " << tha.joinable() << endl;
return 0;
}
多线程下的全局变量
如果声明一个整型全局变量,再利用多个线程对其进行++ *** 作,是否会得到预期的答案?
#include
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(1));
}
}
int main(void)
{
std::thread th1(Print, 1);
std::thread th2(Print, 2);
th1.join();
th2.join();
cout << "main end" << endl;
return 0;
}
1. 加入互斥量std::metux
- 锁定互斥,若互斥不可用则阻塞
void lock();
锁定互斥。若另一线程已锁定互斥,则到 lock 的调用将阻塞执行,直至获得锁。
若 lock 为已占有 mutex 的线程调用,则行为未定义:例如,程序可能死锁。鼓励能检测非法使用的实现抛出以 resource_deadlock_would_occur 为错误条件的 std::system_error ,而不是死锁。
同一互斥上先前的 unlock() *** 作同步于(定义于 std::memory_order )此 *** 作。
- 解锁互斥
互斥必须为当前执行线程所锁定,否则行为未定义。
此 *** 作同步于(定义于 std::memory_order )任何后继的取得同一互斥所有权的锁 *** 作。
更改上述示例:对自增 *** 作加锁
#include
#include
std::mutex mtx;
int g_num = 0;
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(void)
{
std::thread th1(Print, 1);
std::thread th2(Print, 2);
th1.join();
th2.join();
cout << "main end" << endl;
return 0;
}
运行结果:
互斥量的特点简单理解就是:对于临界资源,只有被锁住的线程可以访问,其他的线程不可以访问。
2. 条件变量包含在头文件#include
condition_variable 类是同步原语,能用于阻塞一个线程,或同时阻塞多个线程,直至另一线程修改共享变量(条件)并通知 condition_variable 。
有意修改变量的线程必须
1.获得 std::mutex (常通过 std::lock_guard )
2.在保有锁时进行修改
3.在 std::condition_variable 上执行 notify_one 或 notify_all .
示例1:
#include
#include
#include
#include
#include
using namespace std;
std::condition_variable cv;
std::mutex mtx;
int isReady = 0;
const int n = 10;
void Print_A()
{
std::unique_lock<std::mutex> lock(mtx);
int i = 0;
while (i < n)
{
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(mtx);
int i = 0;
while (i < n)
{
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(mtx);
int i = 0;
while (i < n)
{
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(void)
{
thread tha(Print_A);
thread thb(Print_B);
thread thc(Print_C);
tha.join();
thb.join();
thc.join();
return 0;
}
先来介绍出现的函数:
- wait() —— 阻塞当前线程,直到条件变量被唤醒
void wait( std::unique_lock<std::mutex>& lock );
wait 导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,可选地循环直至满足某谓词(bool值)。
原子地解锁 lock ,阻塞当前执行线程,并将它添加到于 *this 上等待的线程列表。线程将在执行 notify_all() 或 notify_one() 时被解除阻塞。解阻塞时,无关乎原因, lock 再次锁定且 wait 退出。
- notify_all() —— 通知所有等待的线程
解阻塞全部当前等待于 *this 的线程
那么图示为:
1.三个线程都处于就绪状态
2.假设线程C先获得互斥量,那么C运行,循环时判断 isReady是否等于2,不等于2,那么将当前线程C挂起到条件变量的阻塞队列中(线程列表),并释放互斥量;释放后假设 A B两线程,B先获得互斥量,那么B运行,循环判断isReady是否为1,不为1,那么把B挂起到阻塞队列,释放互斥量;然后只剩A线程,A线程直接退出循环,让isReady为1,然后唤醒所有线程。
3.唤醒 B C线程后,线程A还在继续运行,再次循环时判断 isready是否为0,不为0,所以线程A挂起到线程列表里,并释放互斥量;假设C先获得互斥量,循环时又被放入线程列表,然后B获得互斥量,可以继续执行了,然后让 isReady变为2,再次唤醒所有线程。。。。
运行结果:
示例2:让两个线程交替打印奇数和偶数
std::condition_variable cv;
std::mutex mtx;
int number = 1;
void Print_A()
{
std::unique_lock<std::mutex> lock(mtx);
while (number <= 20)
{
while (number % 2 == 0)
{
cv.wait(lock);
}
if (number <= 20)
{
cout << "A thread: " << number << endl;
}
number += 1;
cv.notify_one();
}
}
void Print_B()
{
std::unique_lock<std::mutex> lock(mtx);
while (number <= 20)
{
while (number % 2 == 1)
{
cv.wait(lock);
}
if (number <= 20)
{
cout << "B thread: " << number << endl;
}
number += 1;
cv.notify_one();
}
}
int main(void)
{
std::thread tha(Print_A);
std::thread thb(Print_B);
tha.join();
thb.join();
return 0;
}
也可以利用Lambda表达式来写:
void Print_A()
{
std::unique_lock<std::mutex> lock(mtx);
while (number <= 20)
{
/*while (number % 2 == 0)
{
cv.wait(lock);
}*/
cv.wait(lock, []()->bool {return number % 2 == 1; });
if (number <= 20)
{
cout << "A thread: " << number << endl;
}
number += 1;
cv.notify_one();
}
}
void Print_B()
{
std::unique_lock<std::mutex> lock(mtx);
while (number <= 20)
{
/*while (number % 2 == 1)
{
cv.wait(lock);
}*/
cv.wait(lock, []()->bool {return number % 2 == 0; });
if (number <= 20)
{
cout << "B thread: " << number << endl;
}
number += 1;
cv.notify_one();
}
}
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)