TransWikia.com

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

Stack Overflow Asked by Ting Wang on December 20, 2020

I’m learning multi-thread coding using c++. What I need to do is continuously read word from keyboard, and pass it to a data thread for data processing. I used global variable word[] to pass the data. When word[0] != 0 means a new input from keyboard. And the data thread will set word[0] to 0 once it read the data. It works! But I’m not sure if it safe or not, or there are better ways to do this. Here is my code:

#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")] = '';     //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;
}

4 Answers

In modern CPP multithreading, u should be using condition_variable, mutex, and queue to handle this. the mutex prevents mutual reach to the queue and the condition variable makes the reader thread sleep until the writer writes what it write. the following is an example

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 processedn";
    }
    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;
}

Correct answer by asmmo on December 20, 2020

What you are looking for is a message queue. This needs mutex and condition variable.

Here is one on github (not mine but it popped up when I searched) https://github.com/khuttun/PolyM

and another

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

I will get told off for posting links, but I am not going to type the entire code here and github's not going anywhere soon

Answered by pm100 on December 20, 2020

There are a few problems with your approach:

  • This method is not scalable. What if you have more than 1 processing thread?
  • You would need a mutex to synchronise read-write access to the memory stored by word. At the scale of this example, not a big deal. In a "serious" application you might not have the luxury of waiting till you get the data thread stops processing. In that case, you might be tempted to remove the while(word[0]) but that is unsafe.
  • You fire off a "daemon" thread (not exactly but close enough) to handle your computations. Most of the time the thread is waiting for your input and cannot proceed without it. This is inefficient, and modern C++ gives you a way around it without explicitly handling raw threads using std::async paradigm.
#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 : %sn", result.c_str());
    }

    return 0;
}

Something like this in my opinion is the better approach. std::async will launch a thread (if std::launch::async option is specified) and return a waitable future. The computation will continue in the background, and you can do other work in the main thread. When you need to get the result of your computation, you can get() the result of the future(btw the future can be void too).

Also there are a lot of C-isms in your C++ code. Unless there is a reason to do so, why would you not use std::string?

Answered by Roy2511 on December 20, 2020

The problem with this type of multi threading implementation is busy waiting. The input reader & the data consumer both are busy waiting and wasting the cpu cycles. To overcome this you need Semaphore.

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();
    }
}

In addition this solution will work only for a single data consumer thread. But for multiple data consumer threads you'll need thread safe buffer queue and proper implementation of producer - consumer problem. See below blog links for additional information to solve this problem: Thread safe buffer queue: https://codeistry.wordpress.com/2018/03/08/buffer-queue-handling-in-multithreaded-environment/

Producer - consumer problem: https://codeistry.wordpress.com/2018/03/09/unordered-producer-consumer/

Answered by Ashish Khurange on December 20, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP