-1

A function that is repeatedly called should do multiple tasks:

  1. request a vector from a server.
  2. calculate a new vector.
  3. send this latter to a server.
  4. append the new vector summed to the downloaded one to a FIFO buffer.

Currently, the code that I have implemented starts two new threads inside that function, and wait for their termination - by joining them - before the end of the function. Here is an explanatory code excerpt:

void InStream::requestVectorThread(InStream& me, std::vector<T>& buff) {
    // Do something to request the vector and copy to the buff vector...
}  // Static method...

void InStream::requestVector(std::vector<T>& buff) {
    myThread = std::thread(requestVectorThread, std::ref(*this), std::ref(buff))
}

// Similar implementation for OutStream::sendVector()...

void myFunction() {
    myInStream.requestVector(sharedInBuff);  // Request vector and write it to a shared buffer
    std::vector<T> newVect(1024);
    for (int sample = 0; sample < newVect.size(); ++sample) {
        // Compute the new vector and sum it to the downloaded one...
        // Copy it to the FIFO...
    }
    myInStream.joinThread();
    myOutStream.sendAudio(sharedOutBuff);  // Send the new vector only...
    myOutStream.joinThread();
}

Since the function is called dozens of times in one seconds, dozens of new threads are started and joined in the same time interval.

So, I would like to ask you: is this a good way to do the multiple tasks initially listed? Or would it be better (both for performance and safety) to use just a single thread - that is started at initialisation - with a sleep and wake-up mechanism?

The previously shown code block should became something like this:

void InStream::requestVectorThread(InStream& me, std::vector<T>& buff) {
    while (me.active) {  // Boolean member...
        std::unique_lock<std::mutex> lock(me.myMutex);
        me.myCondVar.wait(lock);
        // Do something to request the vector and copy to the buff vector...
    }
}  // Static method...

void InStream::initRequestVector(std::vector<T>& buff) {
    myThread = std::thread(requestVectorThread, std::ref(*this), std::ref(buff))
}

void InStream::wakeUpThread() {
    if (active) {
        std::unique_lock<std::mutex> lock(myMutex);
        myCondVar.notify_one();
    }
}

// Similar implementation for OutStream::sendVector()...

voit init() {
    myInStream.initRequestVector(sharedInBuff);  // Start the thread and make it sleep...
    active = true;
}

void myFunction() {
    myInStream.wakeUpThread();  // Make the vector request thread running...
    std::vector<T> newVect(1024);
    for (int sample = 0; sample < newVect.size(); ++sample) {
        // Compute the new vector and sum it to the downloaded one, stored in the shared buffer...
        // Copy it to the FIFO...
    }
    myOutStream.wakeUpThread();  // Make the vector send thread running...
}
  • 4
    In general, creating and destroying threads is relatively expensive, depending on the size of the tasks each thread carries out. It is better to have some worker threads that wait on a "job queue", execute each job in the queue, and flag the job as complete. – BobDalgleish Mar 7 at 20:14
1

First you are trying to nest asynchronous behaviour within a synchronous behaviour. This introduces synchronisation overheads, and depending on the systems load may actually be making more work for no gain.

  • Do you need to return the results now, or can you on forward the results when available?
  • Are you using a good algorithm for solving the problem quickly? If its performant enough you can probably ditch the threading, or at-least you have confirmed that it is the only path forward.
  • Have you a baseline single-thread implementation with which to compare actual performance against?

That being said if you are thinking in terms of threads, you are looking at the problem way too low level. There are a wealth of parallelisation models that work really well: Actors, Tasks, Workflows, etc...

I think the most direct approach would be a task/workflow solution. There are numerous libraries out there that help, two that come to mind are: GCD and TBB.

  • The only limitation for the above mentioned function is that it needs the listed tasks to be executed in a very little time interval (some milliseconds). Do you suggest to also try a single-thread implementation, despite time needed by the request to server is not properly predictable? – rudicangiotti Mar 8 at 19:19
  • Depends on how fine grain the tasks are. If each task can be broken down so that the asynchronous aspects such as network/ipc operations can be started and recieved by a later task in the list, then it might just be faster than a parralelel version. Also having a single threaded version allows you to more easily test complex use cases as they are more deterministic, and allows a baseline for actual speedup analysis. Finally i'm hereing request server, until it responds some work simply cannot be done, using a future, or a task/workflow would be even easier. – Kain0_0 Mar 9 at 23:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.