Is there a way to use std::condition_variable::wait(lock, pred) without a lambda?

huangapple go评论109阅读模式
英文:

Is there a way to use std::condition_variable::wait(lock, pred) without a lambda?

问题

我找到了在线上解释如何使用std::condition_variable解决生产者-消费者问题的代码:使用C++中的条件变量解决生产者-消费者问题

#include <condition_variable> // std::condition_variale
#include <cstdlib>
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;

std::mutex g_mutex;
std::condition_variable g_cv;

bool g_ready = false;
int g_data = 0;

int produceData() {
  int randomNumber = rand() % 1000;
  std::cout << "produce data: " << randomNumber << "\n";
  return randomNumber;
}

void consumeData(int data) { std::cout << "receive data: " << data << "\n"; }

void consumer() {
  int data = 0;
  while (true) {
    std::unique_lock<std::mutex> ul(g_mutex);
    g_cv.wait(ul, []() { return g_ready; });
    consumeData(g_data);
    g_ready = false;
    ul.unlock();
    g_cv.notify_one();
  }
}

void producer() {
  while (true) {
    std::unique_lock<std::mutex> ul(g_mutex);
    g_data = produceData();
    g_ready = true;
    ul.unlock();
    g_cv.notify_one();
    ul.lock();
    g_cv.wait(ul, []() { return g_ready == false; });
  }
}

void consumerThread(int n) { consumer(); }

void producerThread(int n) { producer(); }

int main() {
  int times = 100;
  std::thread t1(consumerThread, times);
  std::thread t2(producerThread, times);
  t1.join();
  t2.join();
  return 0;
}

我的代码有效,但我的编码标准规定我不允许使用lambda表达式。我如何在wait()中不使用lambda表达式来使用此代码?

我尝试了以下方法,但它没有起作用:

  1. 使用普通的bool
wait(g_ready)
英文:

I found this code online explaining how to use a std::condition_variable to solve the Producer-Consumer Problem: Producer-Consumer Problem Using Condition Variable in C++

#include <condition_variable> // std::condition_variale
#include <cstdlib>
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;

std::mutex g_mutex;
std::condition_variable g_cv;

bool g_ready = false;
int g_data = 0;

int produceData() {
  int randomNumber = rand() % 1000;
  std::cout << "produce data: " << randomNumber << "\n";
  return randomNumber;
}

void consumeData(int data) { std::cout << "receive data: " << data << "\n"; }

void consumer() {
  int data = 0;
  while (true) {
    std::unique_lock<std::mutex> ul(g_mutex);
    g_cv.wait(ul, []() { return g_ready; });
    consumeData(g_data);
    g_ready = false;
    ul.unlock();
    g_cv.notify_one();
  }
}

void producer() {
  while (true) {
    std::unique_lock<std::mutex> ul(g_mutex);
    g_data = produceData();
    g_ready = true;
    ul.unlock();
    g_cv.notify_one();
    ul.lock();
    g_cv.wait(ul, []() { return g_ready == false; });
  }
}

void consumerThread(int n) { consumer(); }

void producerThread(int n) { producer(); }

int main() {
  int times = 100;
  std::thread t1(consumerThread, times);
  std::thread t2(producerThread, times);
  t1.join();
  t2.join();
  return 0;
}

My code works, but my coding standard says I'm not allowed to use lambdas.
How can I use this code without using lambdas in wait()?

I've tried the following, but it didn't work:

  1. use a normal bool
wait(g_ready)

答案1

得分: 8

Disallowing lambdas in a coding standard has been criticized by commenters. Lambdas are essentially a convenient way to create types with a call operator, which can also be done manually.

使用可调用结构的解决方案

Lambda表达式:

[]() { return g_ready; }

大致对应于:

struct g_ready_checker {
    bool operator()() const noexcept {
        return g_ready;
    }
};

然后可以执行以下操作:

g_cv.wait(ul, g_ready_checker{});

我们还可以将g_ready_checker转化为一个带有<bool DesiredValue>参数的类模板,或者将g_ready的值存储为类成员变量。

使用自由函数的解决方案

或者,由于这个lambda没有捕获任何东西,您还可以编写一个自由函数,可能作为一个模板:

template <bool DesiredValue>
bool is_g_ready() {
    return g_ready == DesiredValue;
}

/* ... */
g_cv.wait(ul, &is_g_ready<true>);
/* ... */
g_cv.wait(ul, &is_g_ready<false>);

在这里,我们为wait提供了一个谓词,它是一个函数指针。请注意,std::condition_variable_waitPredicate模板参数不仅限于lambda,它接受任何可调用的东西,返回值可转换为bool

英文:

As commenters have pointed out, disallowing lambdas is a dubious decision for a coding standard. Regardless, lambdas are just convenience syntax for creating types with a call operator, which you could do by hand.

Solution with callable struct

The lambda expression:

[]() { return g_ready; }

roughly translates into

struct g_ready_checker {
    bool operator()() const noexcept {
        return g_ready;
    }
};

You could then do the following:

g_cv.wait(ul, g_ready_checker{});

We can also turn g_ready_checker into a class template with a &lt;bool DesiredValue&gt; parameter, or we could store the value g_ready is meant to have as a class member.

Solution with free function

Or alternatively, since this lambda isn't capturing anything, you could also write a free function, possibly as a template:

template &lt;bool DesiredValue&gt;
bool is_g_ready() {
    return g_ready == DesiredValue;
}

/* ... */
g_cv.wait(ul, &amp;is_g_ready&lt;true&gt;);
/* ... */
g_cv.wait(ul, &amp;is_g_ready&lt;false&gt;);

Here, we provide wait with a predicate which is a function pointer. Note that the Predicate template parameter of std::condition_variable_wait is not restricted to lambdas, it accepts anything callable which returns something convertible bool.

答案2

得分: 4

以下是已翻译的内容:

这里有一种避免使用 lambda 表达式的方法。

鉴于 std::condition_variable::wait 提供了以下两种重载版本:

void wait( std::unique_lock<std::mutex>& lock );

template< class Predicate >
void wait( std::unique_lock<std::mutex>& lock, Predicate stop_waiting );

如果您有一个 std::condition_variable cv,那么调用 cv.wait(lock, stop_waiting);
等同于:

while (!stop_waiting()) {
    cv.wait(lock);
}

因此,只需不提供第二个参数 stop_waiting(在其中可以提供 lambda 表达式),而是调用 wait(lock) 并在循环中手动检查条件。将 g_cv.wait(ul, []() { return g_ready; }); 更改为不使用 lambda 将简单地如下所示:

while(!g_ready) {
    g_cv.wait(ul);
}

参考文档

英文:

Here's one way to avoid a lambda.

Given that std::condition_variable::wait offers both these overloads:

void wait( std::unique_lock&lt;std::mutex&gt;&amp; lock );

template&lt; class Predicate &gt;
void wait( std::unique_lock&lt;std::mutex&gt;&amp; lock, Predicate stop_waiting );

If you have some std::condition_variable cv, then calling cv.wait(lock, stop_waiting);
is equivalent to:

while (!stop_waiting()) {
    cv.wait(lock);
}

So just don't provide the second argument stop_waiting (where one might provide a lambda) and instead call wait(lock) and check your condition manually in a loop. Changing g_cv.wait(ul, []() { return g_ready; }); to not use a lambda would simply look like this:

while(!g_ready) {
    g_cv.wait(ul);
}

refer to the docs

huangapple
  • 本文由 发表于 2023年6月9日 02:58:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76434942.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定