When to choose a different IPC instead of direct memory access for cross-threading

Since threads of the same process have the same address space, we can transfer data between these threads through direct access to memory and mutexes, then in this context I have the following questions:

  • Are global variables and mutexes enough for cross-threading?
  • if it is incorrect for QUESTION 1, in what context should we choose a different IPC instead of direct memory access? Or, say, in what context are other IPC tools more suitable than using global variables and mutexes?

Thanks.

UPDATE
Thanks to @ssyam for pointing out the wrong statement about "global variables."
I want to add another section, in addition to correcting the original paragraph, because a lot of comments were made in this paragraph.

+4
source share
4 answers

Well, global variables are bad in single-threaded code and in multi-threaded code, they usually become a serious problem. Even if synchronization is performed correctly by mutual interference, they tend to become a bottleneck. In addition, mutexes are usually insufficient for cross-threading. Usually you need at least variable conditions as well.

However, in multi-threaded applications, it is wise to transfer data between threads in memory. As a rule, I found it impractical to deal with explicit blocking. The code is generally less complex and more efficient at transmitting data similar to a messaging system, even if the messages are data structures in memory. In this sense, a message at any time is used by only one thread, and the only blocking occurs in the means of transmission of messages.

0
source

No need for global variables. Remember that thread routines can take parameters, so this can be any variable, including dynamically allocated ones.

Usually you need to โ€œwrapโ€ the stream in the class, something like strings:

struct Thread { Thread() : m_thread(&Thread::run, this) {} void run() { // access the current object member variables, eg: do_something_with(m_myvar); } Object m_myvar; std::thread m_thread; }; 

But if we put aside this small detail of global variables, your No. 1 will be right ... Communication through variables (whether message queues, booleans, whatever), protected by a mutex and optionally condition_variable (to act as an awakening trigger) is almost always the way to go.

I for almost always ends up using thread-safe message queues (i.e. std::queue + mutex + condition_variable ) for communication between threads (producer / consumer pattern), this is a very efficient way to isolate threads and allow them to communicate.


In fact, there are very few cases where in one process something more than direct access to memory makes sense.

All that I can think about now, if you already have some working interprocessor code (for example, sockets or shared memory), you can reuse this code to provide a uniform interface in the process or interprocess communication. But do not be fooled, it will be less effective than direct access to memory. However, the benefits of a consistent interface can easily overcome the loss of efficiency. IMHO, you really need to deal with such things in each case.

+1
source

In (1) I agree with Dietmar Kรผhl that variable conditions are part of this minimal set.

On (2) I prefer to choose IPC when I can afford the small overhead cost (mainly a system call and some data copying) for the convenience and flexibility that comes with them. The pipe, message queue, domain socket, etc., They are all equipped with atomicity and synchronization, and they offer lock, non-blocking or synchronization of read / write depending on the situation. And you can write them to the select statement without doing anything special. This is great power for little cost and does not require reinventing the wheel.

+1
source

Here is a thought:

Even if two threads access the same memory, this does not necessarily mean that they see the same values. If one thread updates the value, the other may see an obsolete value - the value before the update, taken from the local processor cache. To prevent this, you need to synchronize the stream using mutual or other methods.

Regardless of the method, the local cache must be cleared using the so-called "memory barrier", which is a rather expensive operation because it will require all processors to stop everything they do and wait for the operation to complete.

On the other hand, an IPC call does not necessarily require this.

0
source

Source: https://habr.com/ru/post/1498782/


All Articles