I'm trying to implement an multi-thread job, a producer and a consumer, and basically what I want to do is, when consumer finishes the data, it notifies the producer so that producer provides new data.
The tricky part is, in my current impl, producer and consumer both notifies each other and waits for each other, I don't know how to implement this part correctly.
For example, see the code below,
mutex m;
condition_variable cv;
vector<int> Q; // this is the queue the consumer will consume
vector<int> Q_buf; // this is a buffer Q into which producer will fill new data directly
// consumer
void consume() {
while (1) {
if (Q.size() == 0) { // when consumer finishes data
unique_lock<mutex> lk(m);
// how to notify producer to fill up the Q?
...
cv.wait(lk);
}
// for-loop to process the elems in Q
...
}
}
// producer
void produce() {
while (1) {
// for-loop to fill up Q_buf
...
// once Q_buf is fully filled, wait until consumer asks to give it a full Q
unique_lock<mutex> lk(m);
cv.wait(lk);
Q.swap(Q_buf); // replace the empty Q with the full Q_buf
cv.notify_one();
}
}
I'm not sure this the above code using mutex and condition_variable is the right way to implement my idea,
please give me some advice!
The code incorrectly assumes that vector<int>::size() and vector<int>::swap() are atomic. They are not.
Also, spurious wakeups must be handled by a while loop (or another cv::wait overload).
Fixes:
mutex m;
condition_variable cv;
vector<int> Q;
// consumer
void consume() {
while(1) {
// Get the new elements.
vector<int> new_elements;
{
unique_lock<mutex> lk(m);
while(Q.empty())
cv.wait(lk);
new_elements.swap(Q);
}
// for-loop to process the elems in new_elements
}
}
// producer
void produce() {
while(1) {
vector<int> new_elements;
// for-loop to fill up new_elements
// publish new_elements
{
unique_lock<mutex> lk(m);
Q.insert(Q.end(), new_elements.begin(), new_elements.end());
cv.notify_one();
}
}
}
Maybe that is close to what you want to achive. I used 2 conditional variables to notify producers and consumers between each other and introduced variable denoting which turn is now:
#include <ctime>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
template<typename T>
class ReaderWriter {
private:
std::vector<std::thread> readers;
std::vector<std::thread> writers;
std::condition_variable readerCv, writerCv;
std::queue<T> data;
std::mutex readerMutex, writerMutex;
size_t noReaders, noWriters;
enum class Turn { WRITER_TURN, READER_TURN };
Turn turn;
void reader() {
while (1) {
{
std::unique_lock<std::mutex> lk(readerMutex);
while (turn != Turn::READER_TURN) {
readerCv.wait(lk);
}
std::cout << "Thread : " << std::this_thread::get_id() << " consumed " << data.front() << std::endl;
data.pop();
if (data.empty()) {
turn = Turn::WRITER_TURN;
writerCv.notify_one();
}
}
}
}
void writer() {
while (1) {
{
std::unique_lock<std::mutex> lk(writerMutex);
while (turn != Turn::WRITER_TURN) {
writerCv.wait(lk);
}
srand(time(NULL));
int random_number = std::rand();
data.push(random_number);
std::cout << "Thread : " << std::this_thread::get_id() << " produced " << random_number << std::endl;
turn = Turn::READER_TURN;
}
readerCv.notify_one();
}
}
public:
ReaderWriter(size_t noReadersArg, size_t noWritersArg) : noReaders(noReadersArg), noWriters(noWritersArg), turn(ReaderWriter::Turn::WRITER_TURN) {
}
void run() {
int noReadersArg = noReaders, noWritersArg = noWriters;
while (noReadersArg--) {
readers.emplace_back(&ReaderWriter::reader, this);
}
while (noWritersArg--) {
writers.emplace_back(&ReaderWriter::writer, this);
}
}
~ReaderWriter() {
for (auto& r : readers) {
r.join();
}
for (auto& w : writers) {
w.join();
}
}
};
int main() {
ReaderWriter<int> rw(5, 5);
rw.run();
}
Here's a code snippet. Since the worker treads are already synchronized, requirement of two buffers is ruled out. So a simple queue is used to simulate the scenario:
#include "conio.h"
#include <iostream>
#include <thread>
#include <mutex>
#include <queue>
#include <atomic>
#include <condition_variable>
using namespace std;
enum state_t{ READ = 0, WRITE = 1 };
mutex mu;
condition_variable cv;
atomic<bool> running;
queue<int> buffer;
atomic<state_t> state;
void generate_test_data()
{
const int times = 5;
static int data = 0;
for (int i = 0; i < times; i++) {
data = (data++) % 100;
buffer.push(data);
}
}
void ProducerThread() {
while (running) {
unique_lock<mutex> lock(mu);
cv.wait(lock, []() { return !running || state == WRITE; });
if (!running) return;
generate_test_data(); //producing here
lock.unlock();
//notify consumer to start consuming
state = READ;
cv.notify_one();
}
}
void ConsumerThread() {
while (running) {
unique_lock<mutex> lock(mu);
cv.wait(lock, []() { return !running || state == READ; });
if (!running) return;
while (!buffer.empty()) {
auto data = buffer.front(); //consuming here
buffer.pop();
cout << data << " \n";
}
//notify producer to start producing
if (buffer.empty()) {
state = WRITE;
cv.notify_one();
}
}
}
int main(){
running = true;
thread producer = thread([]() { ProducerThread(); });
thread consumer = thread([]() { ConsumerThread(); });
//simulating gui thread
while (!getch()){
}
running = false;
producer.join();
consumer.join();
}
Not a complete answer, though I think two condition variables could be helpful, one named buffer_empty that the producer thread will wait on, and another named buffer_filled that the consumer thread will wait on. Number of mutexes, how to loop, and so on I cannot comment on, since I'm not sure about the details myself.
Accesses to shared variables should only be done while holding the
mutex that protects it
condition_variable::wait should check a condition.
The condition should be a shared variable protected by the mutex that you pass to condition_variable::wait.
The way to check the condition is to wrap the call to wait in a while loop or use the 2-argument overload of wait (which is
equivalent to the while-loop version)
Note: These rules aren't strictly necessary if you truly understand what the hardware is doing. However, these problems get complicated quickly when with simple data structures, and it will be easier to prove that your algorithm is working correctly if you follow them.
Your Q and Q_buf are shared variables. Due to Rule 1, I would prefer to have them as local variables declared in the function that uses them (consume() and produce(), respectively). There will be 1 shared buffer that will be protected by a mutex. The producer will add to its local buffer. When that buffer is full, it acquires the mutex and pushes the local buffer to the shared buffer. It then waits for the consumer to accept this buffer before producing more data.
The consumer waits for this shared buffer to "arrive", then it acquires the mutex and replaces its empty local buffer with the shared buffer. Then it signals to the producer that the buffer has been accepted so it knows to start producing again.
Semantically, I don't see a reason to use swap over move, since in every case one of the containers is empty anyway. Maybe you want to use swap because you know something about the underlying memory. You can use whichever you want and it will be fast and work the same (at least algorithmically).
This problem can be done with 1 condition variable, but it may be a little easier to think about if you use 2.
Here's what I came up with. Tested on Visual Studio 2017 (15.6.7) and GCC 5.4.0. I don't need to be credited or anything (it's such a simple piece), but legally I have to say that I offer no warranties whatsoever.
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <chrono>
std::vector<int> g_deliveryBuffer;
bool g_quit = false;
std::mutex g_mutex; // protects g_deliveryBuffer and g_quit
std::condition_variable g_producerDeliver;
std::condition_variable g_consumerAccepted;
// consumer
void consume()
{
// local buffer
std::vector<int> consumerBuffer;
while (true)
{
if (consumerBuffer.empty())
{
std::unique_lock<std::mutex> lock(g_mutex);
while (g_deliveryBuffer.empty() && !g_quit) // if we beat the producer, wait for them to push to the deliverybuffer
g_producerDeliver.wait(lock);
if (g_quit)
break;
consumerBuffer = std::move(g_deliveryBuffer); // get the buffer
}
g_consumerAccepted.notify_one(); // notify the producer that the buffer has been accepted
// for-loop to process the elems in Q
// ...
consumerBuffer.clear();
// ...
}
}
// producer
void produce()
{
std::vector<int> producerBuffer;
while (true)
{
// for-loop to fill up Q_buf
// ...
producerBuffer = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// ...
// once Q_buf is fully filled, wait until consumer asks to give it a full Q
{ // scope is for lock
std::unique_lock<std::mutex> lock(g_mutex);
g_deliveryBuffer = std::move(producerBuffer); // ok to push to deliverybuffer. it is guaranteed to be empty
g_producerDeliver.notify_one();
while (!g_deliveryBuffer.empty() && !g_quit)
g_consumerAccepted.wait(lock); // wait for consumer to signal for more data
if (g_quit)
break;
// We will never reach this point if the buffer is not empty.
}
}
}
int main()
{
// spawn threads
std::thread consumerThread(consume);
std::thread producerThread(produce);
// for for 5 seconds
std::this_thread::sleep_for(std::chrono::seconds(5));
// signal that it's time to quit
{
std::lock_guard<std::mutex> lock(g_mutex);
g_quit = true;
}
// one of the threads may be sleeping
g_consumerAccepted.notify_one();
g_producerDeliver.notify_one();
consumerThread.join();
producerThread.join();
return 0;
}
Related
I want to serialize network requests using QNetworkAccessManager. For achieving it i wrote such class:
#ifndef CLIENT_H
#define CLIENT_H
#include <queue>
#include <mutex>
#include <condition_variable>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
class Client : public QObject
{
Q_OBJECT
struct RequestRecord
{
RequestRecord(QString u, int o):url(u),operation(o){}
QString url;
int operation;
};
std::mutex mutex;
std::queue<RequestRecord*> requests;
QNetworkAccessManager *manager;
bool running;
std::condition_variable cv;
public:
Client():manager(nullptr){}
~Client()
{
if(manager)
delete manager;
}
void request_cppreference()
{
std::unique_lock<std::mutex> lock(mutex);
requests.push(new RequestRecord("http://en.cppreference.com",0));
cv.notify_one();
}
void request_qt()
{
std::unique_lock<std::mutex> lock(mutex);
requests.push(new RequestRecord("http://doc.qt.io/qt-5/qnetworkaccessmanager.html",1));
cv.notify_one();
}
void process()
{
manager = new QNetworkAccessManager;
connect(manager,&QNetworkAccessManager::finished,[this](QNetworkReply *reply)
{
std::unique_lock<std::mutex> lock(mutex);
RequestRecord *front = requests.front();
requests.pop();
delete front;
reply->deleteLater();
});
running = true;
while (running)
{
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock);
RequestRecord *front = requests.front();
manager->get(QNetworkRequest(QUrl(front->url)));
}
}
};
#endif // CLIENT_H
As one can see, there are 2 methods for requesting data from network and method process, which should be called in separate thread.
I'm using this class as follows:
Client *client = new Client;
std::thread thr([client](){
client->process();
});
std::this_thread::sleep_for(std::chrono::seconds(1));
client->request_qt();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
client->request_cppreference();
This example illustrate 2 consecutive requests to network from one thread and processing of these request in another. All works fine except my lambda is never called. Requests are sent (checked it using wireshark), but i cannot get replies. What is the cause?
as #thuga suppose the problem was in event loop. My thread always waiting on cv and thus cannot process events, little hack solve the problem:
void process()
{
manager = new QNetworkAccessManager;
connect(manager,&QNetworkAccessManager::finished,[this](QNetworkReply *reply)
{
std::unique_lock<std::mutex> lock(mutex);
RequestRecord *front = requests.front();
requests.pop();
delete front;
qDebug() << reply->readAll();
processed = true;
reply->deleteLater();
});
running = true;
while (running)
{
{
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock);
if(requests.size() > 0 && processed)
{
RequestRecord *front = requests.front();
manager->get(QNetworkRequest(QUrl(front->url)));
processed = false;
QtConcurrent::run([this]()
{
while (running)
{
cv.notify_one();
msleep(10);
}
});
}
}
QCoreApplication::processEvents();
}
}
};
it's not beautiful obvious since it is using 3 threads instead of 2, but it is Qt with this perfect phrase:
QUrl QNetworkReply::url() const Returns the URL of the content
downloaded or uploaded. Note that the URL may be different from that
of the original request. If the
QNetworkRequest::FollowRedirectsAttribute was set in the request, then
this function returns the current url that the network API is
accessing, i.e the url emitted in the QNetworkReply::redirected
signal.
I am a bit new to C++ and especially to multithreading. I would like to keep things non-object oriented for now. I have written the following code, and it compiles, which scares me more than if it would not, having read on the forum that one cannot really use a vector of mutexes. So I was wondering whether the following code was indeed correct or not, and what modifications you would bring. Please keep in mind that I cannot radically change the design of my code.
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <mutex>
using namespace std;
mutex print_mutex;
void print_multithread(string message, mutex& print_mutex) {
std::lock_guard<std::mutex> guard(print_mutex);
cout << message << endl;
}
void local_function(int function_index, vector<string> & comm, mutex &comm_mutex) {
string local_data;
while (true) {
//read the communication data structure
{
std::unique_lock<std::mutex> guard(comm_mutex);
local_data = comm.at(function_index);
}
//do stuff... update local_data
//write the communication data_structure
{
std::unique_lock<std::mutex> guard(comm_mutex);
comm.at(function_index) = local_data;
}
}//end while(true)
}
void global_function(vector<string> & comm, mutex & comm_mutex) {
vector<string> global_data;
while (true) {
//read the communication data structure
{
std::unique_lock<std::mutex> guard(comm_mutex);
global_data = comm;
}
//do stuff... update global_data
//write the communication data_structure
{
std::unique_lock<std::mutex> guard(comm_mutex);
comm = global_data;
}
}
}
int main() {
vector<string> comm = { "init1", "init2" };
mutex comm_mutex;
thread local_fct0(local_function, 0, ref(comm), ref(comm_mutex));
thread local_fct1(local_function, 1, ref(comm), ref(comm_mutex));
thread global_fct(global_function, ref(comm), ref(comm_mutex));
local_fct0.join();
local_fct1.join();
global_fct.join();
cin.ignore();
return 0;
}
Now this compiles, and I believe it to be correct (please let me know if I am wrong). What I am interested now in doing is something like this in the main:
int main() {
vector<vector<string>> comm_matrix = { { "init1A", "init2A" } , {"init1B", "init2B"} };
vector<mutex> comm_mutex(2);
thread local_fct0A(local_function, 0, ref(comm_matrix.at(0)), ref(comm_mutex.at(0)));
thread local_fct1A(local_function, 1, ref(comm_matrix.at(0)), ref(comm_mutex.at(0)));
thread global_fctA(global_function, ref(comm_matrix.at(0)), ref(comm_mutex.at(0)));
thread local_fct0B(local_function, 0, ref(comm_matrix.at(1)), ref(comm_mutex.at(1)));
thread local_fct1B(local_function, 1, ref(comm_matrix.at(1)), ref(comm_mutex.at(1)));
thread global_fctB(global_function, ref(comm_matrix.at(1)), ref(comm_mutex.at(1)));
local_fct0A.join();
local_fct1A.join();
global_fctA.join();
local_fct0B.join();
local_fct1B.join();
global_fctB.join();
cin.ignore();
return 0;
}
Now this compiles again, but I am not sure whether it does what it is supposed to , i.e. that local_fct0A, local_fct1A and global_fctAall use the same mutex stored in comm_mutex.at(0) and similarly for comm_mutex.at(1) (with B). Finally, I will want to do yet another function that is even more global, sort of a conductor for the global_functions that will have signature
void global_global_function(vector<vector<string>> & comm_matrix, vector<mutex> & comm_mutexes) {}
Which would then lock the mutexes in comm_mutexes inside it's body. In the main it would look like:
thread global_global_fct(global_global_function, ref(comm_matrix), ref(comm_mutex));
global_global_fct.join();
I know that this is not the best way to do these things, but unfortunately I do not have the time to become proficient at OOP, so I would like to keep it in functional form.
Thanks in advance, I appreciate any criticism!
Is there a way to start two (or more) C++11 threads and join() the first one that is finished?
An example scenario:
#include <iostream>
#include <thread>
using namespace std;
void prepare_item1() {std::cout << "Preparing 1" << std::endl;}
void consume_item1() {std::cout << "Consuming 1" << std::endl;}
void prepare_item2() {std::cout << "Preparing 2" << std::endl;}
void consume_item2() {std::cout << "Consuming 2" << std::endl;}
int main()
{
std::thread t1(prepare_item1);
std::thread t2(prepare_item2);
t1.join();
consume_item1();
t2.join();
consume_item2();
return 0;
}
I would have liked to do something like that instead:
int main()
{
std::thread t1(prepare_item1);
std::thread t2(prepare_item2);
finished_id=join_any(t1,t2)
if (finished_id==1)
{
consume_item1();
...
}
else if (finished_id==2)
{
consume_item2();
...
}
return 0;
}
Also, I would like the solution to be blocking, similar to the t.join() function.
Note: The real reason I need this is that I have two different blocking functions from which I receive commands, and whenever any of them is ready I would like to process the first command that arrives and continue to the next one when it is done. (sequential processing of commands from two parallel sources)
Thanks!
Here is a thread-safe multi-producer multi-consumer queue:
template<class T>
struct safe_queue {
std::deque<T> data;
std::atomic<bool> abort_flag = false;
std::mutex guard;
std::condition_variable signal;
template<class...Args>
void send( Args&&...args ) {
{
std::unique_lock<std::mutex> l(guard);
data.emplace_back(std::forward<Args>(args)...);
}
signal.notify_one();
}
void abort() {
abort_flag = true; // 1a
{ std::unique_lock<std::mutex>{guard}; }
signal.notify_all(); // 1b
}
std::experimental::optional<T> get() {
std::unique_lock<std::mutex> l(guard);
signal.wait( l, [this]()->bool{ // 2b
return !data.empty() || abort_flag.load(); // 2c
});
if (abort_flag.load()) return {};
T retval = std::move(data.front());
data.pop_front();
return retval;
}
};
have the threads shove data into the queue, and the main thread do a .get() on it.
If abort() is called, all waiting threads are woken up with an "empty" value from .get().
It uses std::experimental::optional, but you can replace that with something else (throw on abort? Whatever).
Code modified slightly from this other answer. Note that I think the other answer has some errors in it, which I corrected above, and attempts to solve a different problem.
The message you send could be the id of the thread that is ready to be waited upon, for example, or the work it has completed, or whatever.
Below is an attempt at a multiple reader / writer shared data which uses std::atomics and busy waits instead of mutex and condition variables to synchronize between readers and writers. I am puzzled as to why the asserts in there are being hit. I'm sure there's a bug somewhere in the logic, but I'm not certain where it is.
The idea behind the implementation is that threads that read are spinning until the writer is done writing. As they enter the read function they increase m_numReaders count and as they are waiting for the writer they increase the m_numWaiting count.
The idea is that the m_numWaiting should then always be smaller or equal to m_numReaders, provided m_numWaiting is always incremented after m_numReaders and decremented before m_numReaders.
There shouldn't be a case where m_numWaiting is bigger than m_numReaders (or I'm not seeing it) since a reader always increments the reader counter first and only sometimes increments the waiting counter and the waiting counter is always decremented first.
Yet, this seems to be whats happening because the asserts are being hit.
Can someone point out the logic error, if you see it?
Thanks!
#include <iostream>
#include <thread>
#include <assert.h>
template<typename T>
class ReadWrite
{
public:
ReadWrite() : m_numReaders(0), m_numWaiting(0), m_writing(false)
{
m_writeFlag.clear();
}
template<typename functor>
void read(functor& readFunc)
{
m_numReaders++;
std::atomic<bool>waiting(false);
while (m_writing)
{
if(!waiting)
{
m_numWaiting++; // m_numWaiting should always be increased after m_numReaders
waiting = true;
}
}
assert(m_numWaiting <= m_numReaders);
readFunc(&m_data);
assert(m_numWaiting <= m_numReaders); // <-- These asserts get hit ?
if(waiting)
{
m_numWaiting--; // m_numWaiting should always be decreased before m_numReaders
}
m_numReaders--;
assert(m_numWaiting <= m_numReaders); // <-- These asserts get hit ?
}
//
// Only a single writer can operate on this at any given time.
//
template<typename functor>
void write(functor& writeFunc)
{
while (m_writeFlag.test_and_set());
// Ensure no readers present
while (m_numReaders);
// At this point m_numReaders may have been increased !
m_writing = true;
// If a reader entered before the writing flag was set, wait for
// it to finish
while (m_numReaders > m_numWaiting);
writeFunc(&m_data);
m_writeFlag.clear();
m_writing = false;
}
private:
T m_data;
std::atomic<int64_t> m_numReaders;
std::atomic<int64_t> m_numWaiting;
std::atomic<bool> m_writing;
std::atomic_flag m_writeFlag;
};
int main(int argc, const char * argv[])
{
const size_t numReaders = 2;
const size_t numWriters = 1;
const size_t numReadWrites = 10000000;
std::thread readThreads[numReaders];
std::thread writeThreads[numWriters];
ReadWrite<int> dummyData;
auto writeFunc = [&](int* pData) { return; }; // intentionally empty
auto readFunc = [&](int* pData) { return; }; // intentionally empty
auto readThreadProc = [&]()
{
size_t numReads = numReadWrites;
while (numReads--)
{
dummyData.read(readFunc);
}
};
auto writeThreadProc = [&]()
{
size_t numWrites = numReadWrites;
while (numWrites--)
{
dummyData.write(writeFunc);
}
};
for (std::thread& thread : writeThreads) { thread = std::thread(writeThreadProc);}
for (std::thread& thread : readThreads) { thread = std::thread(readThreadProc);}
for (std::thread& thread : writeThreads) { thread.join();}
for (std::thread& thread : readThreads) { thread.join();}
}
I need to implement producer-consumer problem in my project. N consumers and M producers will be created. A producer will use publish(v) call to reach v data to consumer. A consumer will use get_data(v) call to get a copy of data v. I really don't know how to implement it. Please help me.
I am going to use C to implement it. I will create n process for consumers and m process for producers. If a producer publish a data, other producers can not do it until all consumers get it. I will use semaphores and shared memory to exchange data.
I found something which does similar job. But it is using threads but i need process instead. How can i change this.
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#define BUFF_SIZE 4
#define FULL 0
#define EMPTY 0
char buffer[BUFF_SIZE];
int nextIn = 0;
int nextOut = 0;
sem_t empty_sem_mutex; //producer semaphore
sem_t full_sem_mutex; //consumer semaphore
void Put(char item)
{
int value;
sem_wait(&empty_sem_mutex); //get the mutex to fill the buffer
buffer[nextIn] = item;
nextIn = (nextIn + 1) % BUFF_SIZE;
printf("Producing %c ...nextIn %d..Ascii=%d\n",item,nextIn,item);
if(nextIn==FULL)
{
sem_post(&full_sem_mutex);
sleep(1);
}
sem_post(&empty_sem_mutex);
}
void * Producer()
{
int i;
for(i = 0; i < 10; i++)
{
Put((char)('A'+ i % 26));
}
}
void Get()
{
int item;
sem_wait(&full_sem_mutex); // gain the mutex to consume from buffer
item = buffer[nextOut];
nextOut = (nextOut + 1) % BUFF_SIZE;
printf("\t...Consuming %c ...nextOut %d..Ascii=%d\n",item,nextOut,item);
if(nextOut==EMPTY) //its empty
{
sleep(1);
}
sem_post(&full_sem_mutex);
}
void * Consumer()
{
int i;
for(i = 0; i < 10; i++)
{
Get();
}
}
int main()
{
pthread_t ptid,ctid;
//initialize the semaphores
sem_init(&empty_sem_mutex,0,1);
sem_init(&full_sem_mutex,0,0);
//creating producer and consumer threads
if(pthread_create(&ptid, NULL,Producer, NULL))
{
printf("\n ERROR creating thread 1");
exit(1);
}
if(pthread_create(&ctid, NULL,Consumer, NULL))
{
printf("\n ERROR creating thread 2");
exit(1);
}
if(pthread_join(ptid, NULL)) /* wait for the producer to finish */
{
printf("\n ERROR joining thread");
exit(1);
}
if(pthread_join(ctid, NULL)) /* wait for consumer to finish */
{
printf("\n ERROR joining thread");
exit(1);
}
sem_destroy(&empty_sem_mutex);
sem_destroy(&full_sem_mutex);
//exit the main thread
pthread_exit(NULL);
return 1;
}
I'd suggest you to make a plan and start reading. For example:
Read about how to create and manage threads. Hint: pthread.
Think how will the threads communicate - usually they use common data structure. Hint: message queue
Think how to protect the data structure, so both threads can read and write safely. Hint: mutexes.
Implement consumer and producer code.
Really, if you want more information you have to work a bit and ask more specific questions. Good luck!