中的std ::线程使用无限循环来增加和显示的值
考虑下面的简单代码:中的std ::线程使用无限循环来增加和显示的值
using ms = std::chrono::milliseconds; int val = 0;
for(;;)
{
std::cout << val++ << ' ';
std::this_thread::sleep_for(ms(200));
}
我们看到,我们无限打印随后的数字每次0.2秒。
现在,我想使用助手类和多线程实现相同的逻辑。我的目标是能够运行类似这样:
int main() {
Foo f;
std::thread t1(&Foo::inc, f);
std::thread t2(&Foo::dis, f);
t1.join();
t2.join();
}
Foo::inc()
其中将由1
和Foo::dis()
递增对象f
的成员变量val
将显示相同变量。
由于最初的想法包括递增和打印无限的价值,我会假设这两个函数都必须包含一个无限循环。可能发生的问题是数据竞争 - 读取和增加相同的变量。为了防止我决定使用std::mutex
。
我实现Foo
的思路如下:
class Foo { int val;
public:
Foo() : val{0} {}
void inc()
{
for(;;){
mtx.lock();
++val;
mtx.unlock();
}
}
void dis()
{
using ms = std::chrono::milliseconds;
for(;;){
mtx.lock();
std::cout << val << ' ';
std::this_thread::sleep_for(ms(200));
mtx.unlock();
}
}
};
显然,这是缺少mtx
对象,所以行
std::mutex mtx;
的正下方#include
书面,宣布mtx
作为全球变量。
据我了解,这个类的定义与上述main()
功能应该发出两个,独立,无限循环,每个将首先锁定互斥体,无论是增量或显示val
和解锁互斥体结合所以其他人可以执行第二个动作。
实际发生的并不是显示0 1 2 3 4...
的顺序,它只是显示0 0 0 0 0...
。我的猜测是我错误地使用了std::mutex::lock
和std::mutex::unlock
,或者我对多线程的基本理解缺乏一些基本知识。
问题是 - 我的逻辑错在哪里?
如何将接近使用辅助类和两个
std::thread
s的同一对象的成员函数这个问题?是否有保证0123'的增量和它的打印将分别使用这种逻辑来一个接一个地发生?即在
val
在显示之前增加两次,否则永远不会出现这种情况,反之亦然?
回答:
当启动一个成员函数一个线程,你需要通过对象的地址,而不是对象本身
std::thread t2(&Foo::dis, &f);
请注意,这仍然无法打印1 2 3 4 ..你需要有增量操作和打印替代方案。
#include <thread> #include<iostream>
#include <mutex>
std::mutex mtx1, mtx2;
class Foo {
int val;
public:
Foo() : val{0} { mtx2.lock(); }
void inc()
{
for(;;){
mtx1.lock();
++val;
mtx2.unlock();
}
}
void dis()
{
using ms = std::chrono::milliseconds;
for(;;){
mtx2.lock();
std::cout << val <<std::endl;
std::this_thread::sleep_for(ms(200));
mtx1.unlock();
}
}
};
int main()
{
Foo f;
std::thread t1(&Foo::inc, &f);
std::thread t2(&Foo::dis, &f);
t1.join();
t2.join();
}
而且看看http://en.cppreference.com/w/cpp/thread/condition_variable
回答:
互斥是不是一个信号。这不公平。你可以解锁然后重新锁定一个互斥锁,而等待它的人永远不会注意到。
它保证只有一个线程被锁定。
您的任务将其分解为两个线程似乎毫无意义。使用睡眠也是一个坏主意,因为打印花费的时间不明,使显示之间的时间间隔漂移不可预测的数量。 (A)不想这样做,并且失败(B)使用条件变量。一个线程每X次增加一个值(基于固定的开始时间,而不是基于X的延迟),然后签署条件变量。它在等待时不占用互斥量。
另一个线程等待条件变量和计数器值改变。当它醒来时,它复制计数器,解锁,打印一次,更新最后看到的值,然后再等待条件变量(和值变化)。
对此的一个温和的好处是,如果io是可笑的缓慢或阻塞,柜台不断增加,所以其他消费者可以使用它。
struct Counting { int val = -1; // optionally atomic
std::mutex mtx;
std::condition_variable cv;
void counting() {
while(true){
{
auto l=std::unique_lock<std::mutex>(mtx);
++val; // even if atomic, val must be modified while or before the mtx is held and before the notify.
}
// or notify all:
cv.notify_one(); // no need to hold lock here
using namespace std::literals;
std::this_thread::sleep_for(200ms); // ideally wait to an absolute time instead of delay here
}
}
void printing() {
int old_val=-1;
while(true){
int new_val=[&]{
auto lock=std::unique_lock<std::mutex>(mtx);
cv.wait(lock, [&]{ return val!=old_val; }); // only print if we have a new value
return val;
}();// release lock, no need to hold it while printing
std::cout << new_val << std::endl; // endl flushes. Note there are threading issues streaming to cout like this.
old_val=new_val; // update last printed value
}
}
};
如果一个线程正在打印另一个计数,您将基本得到您想要的。
回答:
您正在睡眠,线程已锁定,阻止其他线程在大多数时间运行。
void dis() {
using ms = std::chrono::milliseconds;
for(;;){
mtx.lock();
std::cout << val << ' ';
std::this_thread::sleep_for(ms(200)); // this is still blocking the other thread
mtx.unlock();
}
}
试试这个:
void dis() {
using ms = std::chrono::milliseconds;
for(;;){
mtx.lock();
std::cout << val << ' ';
mtx.unlock(); // unlock to allow the other thread to progress
std::this_thread::sleep_for(ms(200));
}
}
此外,而不是使用全局std::mutex
,你可以将其添加为您的类的成员。
如果您想同步线程以产生偶数输出的数字,每次只输出一个数字,那么您需要类似于std::condition_variable的东西,以便每个线程在完成作业的一部分时可以发信号通知另一个线程(线程一个 - 递增和线程2 - 印刷)。
下面是一个例子:
class Foo { int val;
std::mutex mtx;
std::condition_variable cv;
bool new_value; // flag when a new value is ready
public:
Foo() : val{0}, new_value{false} {}
void inc()
{
for(;;){
std::unique_lock<std::mutex> lock(mtx);
// release the lock and wait until new_value has been consumed
cv.wait(lock, [this]{ return !new_value; }); // wait for change in new_value
++val;
new_value = true; // signal for the other thread there is a new value
cv.notify_one(); // wake up the other thread
}
}
void dis()
{
using ms = std::chrono::milliseconds;
for(;;){
// a nice delay
std::this_thread::sleep_for(ms(200));
std::unique_lock<std::mutex> lock(mtx);
// release the lock and wait until new_value has been produced
cv.wait(lock, [this]{ return new_value; }); // wait for a new value
std::cout << val << ' ' << std::flush; // don't forget to flush
new_value = false; // signal for the other thread that the new value was used
cv.notify_one(); // wake up the other thread
}
}
};
int main(int argc, char** argv)
{
Foo f;
std::thread t1(&Foo::inc, &f);
std::thread t2(&Foo::dis, &f);
t1.join();
t2.join();
}
以上是 中的std ::线程使用无限循环来增加和显示的值 的全部内容, 来源链接: utcz.com/qa/259208.html