中的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()其中将由1Foo::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::lockstd::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

回到顶部