繁体   English   中英

在 C++ 中将数据传递给线程的好方法是什么?

[英]What's the good way to pass data to a thread in c++?

我正在使用 C++ 学习多线程编码。 我需要做的是不断从键盘读取单词,并将其传递给数据线程进行数据处理。 我使用全局变量word[]来传递数据。 word[0] != 0表示来自键盘的新输入。 并且数据线程一旦读取数据就会将word[0]设置为 0 有用! 但我不确定它是否安全,或者有更好的方法来做到这一点。 这是我的代码:

#include <iostream> 
#include <thread>
#include <cstdio>
#include <cstring>

using namespace std;

static const int buff_len = 32;
static char* word = new char[buff_len];


static void data_thread () {         // thread to handle data
  while (1)               
  {
    if (word[0]) {                   // have a new word
      char* w = new char[buff_len];
      strcpy(w, word);
      cout << "Data processed!\n";
      word[0] = 0;                   // Inform the producer that we consumed the word
    }
  }
};

static void read_keyboard () {
  char * linebuf = new char[buff_len];
  thread * worker = new thread( data_thread );
  
  while (1)                                     //enter "end" to terminate the loop
  {
    if (!std::fgets( linebuf, buff_len, stdin)) // EOF?
      return;
    linebuf[strcspn(linebuf, "\n")] = '\0';     //remove new line '\n' from the string
    
    word = linebuf;                             // Pass the word to the worker thread
    while (word[0]);                            // Wait for the worker thread to consume it
  }
  worker->join();                               // Wait for the worker to terminate
}

int main ()
{
  read_keyboard(); 
  return 0;
}

这种类型的多线程实现的问题是忙于等待。 输入读取器和数据消费者都忙于等待并浪费 CPU 周期。 为了克服这个问题,您需要信号量。

Semaphore s_full(0);
Semaphore s_empty(1);

void data_processor ()
{
    while (true) {
        // Wait for data availability.
        s_full.wait();
            // Data is available to you, consume it.
            process_data();
        // Unblock the data producer.
        s_empty.signal();
    }
}

void input_reader()
{
    while (true) {
        // Wait for empty buffer.
        s_empty.wait();
            // Read data.
            read_input_data();
        // Unblock data com=nsumer.
        s.full.signal();
    }
}

此外,此解决方案仅适用于单个数据消费者线程。 但是对于多个数据消费者线程,您将需要线程安全的缓冲区队列和生产者 - 消费者问题的正确实现。 有关解决此问题的其他信息,请参阅以下博客链接:线程安全缓冲区队列: https : //codeistry.wordpress.com/2018/03/08/buffer-queue-handling-in-multithreaded-environment/

生产者-消费者问题: https : //codeistry.wordpress.com/2018/03/09/unordered-producer-consumer/

你的方法有几个问题:

  • 此方法不可扩展。 如果您有 1 个以上的处理线程怎么办?
  • 您需要一个互斥锁来同步对word存储的内存的读写访问。 在这个例子的规模上,没什么大不了的。 在“严重”的应用程序中,您可能无法等到数据线程停止处理。 在这种情况下,您可能想删除while(word[0])但这是不安全的。
  • 您触发一个“守护进程”线程(不完全但足够接近)来处理您的计算。 大多数情况下,线程正在等待您的输入,没有它就无法继续。 这是低效的,现代 C++ 为您提供了一种解决方法,而无需使用std::async范例显式处理原始线程。
#include <future>
#include <string>
#include <iostream>

static std::string worker(const std::string &input)
{
    // assume this is a lengthy operation
    return input.substr(1);
}

int main()
{
    while (true)
    {
        std::string input;
        std::getline (std::cin, input); 
        
        if (input.empty())
            break;
            
        std::future<std::string> fut= std::async(std::launch::async, &worker, input);
        // Other tasks
        // size_t n_stars = count_number_of_stars();
        //
        std::string result = fut.get(); // wait for the task to complete
        printf("Output : %s\n", result.c_str());
    }

    return 0;
}

在我看来,这样的事情是更好的方法。 std::async将启动一个线程(如果指定了std::launch::async选项)并返回一个可等待的future 计算将在后台继续进行,您可以在主线程中进行其他工作。 当您需要获得计算结果时,您可以get() future的结果(顺便说一句, future也可以是void )。

在你的 C++ 代码中也有很多 C-isms。 除非有理由这样做,否则为什么不使用std::string

您正在寻找的是消息队列。 这需要互斥锁和条件变量。

这是github上的一个(不是我的,但在我搜索时弹出了) https://github.com/khuttun/PolyM

而另一个

https://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html

我会因为发布链接而被告知,但我不会在这里输入整个代码,而且 github 不会很快去任何地方

在现代 CPP 多线程中,你应该使用condition_variablemutexqueue来处理这个问题。 互斥锁防止相互访问队列,条件变量使读取器线程休眠,直到写入器写入它所写的内容。 下面是一个例子

static void data_thread (std::queue<char> & dataToProcess, std::mutex & mut, std::condition_variable & cv, std::atomic<bool>& finished) {         // thread to handle data
    std::string readData;
    while (!finished)
    {
        {
            std::unique_lock lock{mut};
            cv.wait(lock, [&] { return !dataToProcess.empty() || finished; });
            if (finished) {
                while (!dataToProcess.empty()){
                    readData += dataToProcess.front();
                    dataToProcess.pop();

                }
            }
            else{
                readData += dataToProcess.front();
                dataToProcess.pop();
            }
        }
        std::cout << "\nData processed\n";
    }
    std::cout << readData;
};

static void read_keyboard () {
    std::queue<char> data;
    std::condition_variable cv;
    std::mutex mut;
    std::atomic<bool> finished = false;
    std::thread worker = std::thread( data_thread, std::ref(data), std::ref(mut), std::ref(cv), std::ref(finished) );
    char temp;
    while (true)                                     //enter "end" to terminate the loop
    {
        if (!std::cin.get(temp)) // EOF?
        {
            std::cin.clear();
            finished = true;
            cv.notify_all();
            break;
        }

        {
            std::lock_guard lock {mut};
            data.push(temp);
        }
        cv.notify_all();
    }
    worker.join();                               // Wait for the worker to terminate
}

int main ()
{
    read_keyboard();
    return 0;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM