Clark Kent
Clark Kent

Reputation: 1176

Multithreading using pthread in C++ with shared variables

I'm new to threading (and C/C++ for that matter), and I'm attempting to use multiple threads to access shared variables.

In the main, I've created a variable char inputarray[100];

Thread 1: This thread will be reading data from stdin in 2 byte bursts, and appending them to the inputarray. (input by feeding a file in)

Thread 2: This thread will be reading data 1 byte at a time, performing a calculation, and putting its data into an output array.

Thread 3: This thread will be outputting data from the output array in 2 byte bursts. (stdout)

I've attempted the input part and got it working by passing a struct, but would like to do it without using a struct, but it has been giving me problems.

If I can get input down, I'm sure I'll be able to use a similar strategy to complete output. Any help would be greatly appreciated.

Below is a rough template for the input thread.

#include <stdio.h>
#include <pthread.h>

using namespace std;

void* input(void* arg) {
    char reading[3];
    fread(reading,1,2,stdin);

    //append to char inputarray[]..???
}

int main() {
    char inputarray[100];
    pthread_t t1;
    pthread_create(&t1, NULL, &input, &inputarray);
    void *result;
    pthread_join(t1,&result);
    return 0;
}  

Upvotes: 1

Views: 3960

Answers (2)

Loki Astari
Loki Astari

Reputation: 264381

You are on the correct track:

As a note the pthreads libraries are C libs so you need to declare the callbacks as C functions:

extern "C" void* input(void* arg);

Personally I would pass the address of the first element:

pthread_create(&t1, NULL, &input, &inputarray[0]);

This then makes your code look like this:

void* input(void* arg) {

    try
    {
       char*  inputarray    = (char*)arg;
       size_t inputLocation = 0;

       // Need to make sure you don't over run the buffer etc...
       while(!finished())
       {
          fread(&inputarray[inputLocation],1,2,stdin);
          inputLocation += 2;
       }
    }
    catch(...){} // Must not let exceptions escape a thread.
    return NULL;
}

The trouble with this style is that you are putting the responsibility for coordination into each individual thread. The writer thread has to check for end the reader thread has to check there is data available etc. All this needs coordination so now you need some shared mutexes and condition variables.

A better choice is to move that responsibility into the object the does the communication. So I would create a class that has the basic operations needed for communication then make its methods do the appropriate checks.

 class Buffer
 {
     public:
        void write(......); //
        void read(.....);   //
     private:
        // All the synchronization and make sure the two threads
        // behave nicely inside the object.
 };

 int main()
 {
       pthread_t threads[3];
       std::pair<Buffer, Buffer>   comms;
       // comms.first     inputToRead
       // comms.second    processesToOutput


       pthread_create(&threads[0], NULL, &readInput, &comms.first);   // Input
       pthread_create(&threads[1], NULL, &procInput, &comms);         // Processing
       pthread_create(&threads[2], NULL, &genOutput, &comms.second);  // Output

       void *result;
       pthread_join(threads[0],&result);
       pthread_join(threads[1],&result);
       pthread_join(threads[2],&result);

 }

As a side note:

Unless there is something very strange about your processing of data. This would probably be faster written as a single threaded application.

Upvotes: 2

nogard
nogard

Reputation: 9706

Several issues:

  1. I think array on stack is very bad choice for shared variable, because it has a fixed size and it's not clear from Thread 2 and 3 where to put new elements or where to read elements from. I would propose to use std::vector or std::deque instead. Initially your container is empty. Then Thread 2 pushes some elements to it. Thread 3 is polling (or waiting on condition variable) container, and once it found new elements - print them

  2. You have to synchronize access to shared variable with mutex (consider pthread mutex, std::mutex or boost::mutex). You might also want to use condition variable to notify Thread 3 about new elements in the queue. But for initial implementation it's not needed.

  3. Do you really have to use pthread primitives? Normally it's much easier and safer (i.e. exception safety) to use std::thread, std::mutex (if you have modern compiler), or boost::thread, boost::mutex otherwise.

Upvotes: 8

Related Questions