从一个线程向另一个线程发出信号的最快方式

时间:2015-07-09 03:17:23

标签: c++ multithreading c++11 std

我的代码与下面的内容非常相似。代码有效,但我很好奇是否有更快(更低的延迟)来向另一个线程发出信号。

// condition_variable::wait (with predicate)
#include <iostream>           // std::cout
#include <thread>             // std::thread, std::this_thread::yield
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable

std::mutex mtx;
std::condition_variable cv;

int cargo = 0;
bool shipment_available()
{
    return cargo!=0;
}

void consume ()
{
    while(1)
    {
        std::unique_lock<std::mutex> lck(mtx);
        cv.wait(lck,shipment_available);
        // consume:
        std::cout << cargo << '\n';
        cargo=0;
    }
}

int main ()
{
    std::thread consumer_thread (consume);

    // produce 10 items when needed:
    int i=0;
    while(1)
    {
        while (shipment_available()) std::this_thread::yield();
        std::unique_lock<std::mutex> lck(mtx);
        cargo = i++;
        cv.notify_one();

        if(i > 100)
            break;
    }

    consumer_thread.join();

    return 0;
}

1 个答案:

答案 0 :(得分:3)

您的示例中存在许多错误,因此我将首先尝试回答其他一些问题,您应该在此之前提出问题。

关于您的示例

首先 - 这个例子没有意义 - 它实际上是同步的:两个线程不同时运行 - 其中一个总是等待另一个。如果在这种情况下 - 性能是您的主要目标 - 您应该将此代码放在单个线程中。

如果您希望使用所有CPU内核来提高性能,则需要使用完全不同的异步方法。您必须使用工作线程的线程池并将任务提供给队列中的任务。这意味着,您必须更改代码的架构,因此 - 它超出了您的问题范围。

然后,通过从另一个线程简单调用thread::join,您的工作线程将不会结束。 thread::join阻塞调用它的线程,直到目标线程退出。因此,您的应用程序将在join调用时挂起,因为您的工作线程永远不会退出while(1)循环。你需要以某种方式告诉它,没有更多的工作,它必须完成。您可以使用我在下面的示例中使用的特殊货物值(例如负值)或停止标志。 std::thread的文档:http://en.cppreference.com/w/cpp/thread/thread

另外,如果使用互斥锁保护某些变量(并且必须执行此操作,如果对它们的访问不是原子的并且它们同时在多个线程中使用),则不应在不获取的情况下使用它们说mutex - 它总会引导你出现某种同步错误。我在while (shipment_available())范围之外的主线程中讨论std::unique_lock。有了这个 - 代码将编译正常,它将正常工作,但有一天,工作线程将写入cargo而主线程正在读取其值,如果访问int在您的平台上不是原子的 - 可能发生意外和完全不可预测的事情。也许它会导致应用程序崩溃,也许会导致读取错误的值。在这个特定的例子中 - 我无法想象会出现什么问题(但是 Mark Lakata 在评论中指出了一种情况),但在一般情况下 - 这是一个可怕的错误,这是难以置信的难以抓住。 C ++ 11线程和同步的精细教程解释了这种陷阱:http://baptiste-wicht.com/categories/c11-concurrency-tutorial.html

关于您的问题:

要提高示例中的性能,可以使用std::atomic对象的无锁方法。 std::atomic的文档:http://en.cppreference.com/w/cpp/atomic/atomic

应该说,通过这种方法 - 线程根本不会休眠,并且将完全占用CPU的两个核心。 yield从资源管理的角度来看稍微好一点,但会增加线程通信的延迟,因此可以将其删除。

代码

#include <iostream>
#include <thread>
#include <atomic>

// make your cargo an atomic object
// it does not need a mutex to sync access to it from several threads
std::atomic<int> cargo = 0;

// you should have some method to stop thread, it wont stop by itself by calling std::thread::join()
// the most simple one - is atomic stop flag.
std::atomic<bool> consumerThreadStopFlag = false;

bool shipment_available()
{
   return cargo!=0;
}

void consume()
{
   while(!consumerThreadStopFlag) // while stop flag is not set
    {
      while(!shipment_available())
         std::this_thread::yield();

      std::cout << cargo << '\n';
      cargo=0;
    }
}

int main ()
{
   std::thread consumer_thread (consume);

   // produce 10 items when needed:
   int i=0;
   while(1)
   {
      while (shipment_available())
         std::this_thread::yield();

      cargo = i++;

   if(i > 100)
      break;
   }

   consumerThreadStopFlag = true; // setup stop flag
   consumer_thread.join();        // wait till thread leaves its while loop

   return 0;
}