How to save managed asynchronous parallel program code (e.g. in C ++)

I am currently working on a server application that should manage collection devices over the network. Because of this, we need to do a lot of parallel programming. Over time, I learned that there are three approaches to exchanging data between processing objects (threads / processes / applications). Unfortunately, all three approaches have their drawbacks .

A) You can execute a synchronous request (call of synchronous function). In this case, the caller waits until the function is processed and a response is received. For instance:

const bool convertedSuccessfully = Sync_ConvertMovie(params); 

The problem is that the caller is idling. Sometimes this is not an option. For example, if the call was triggered by a UI thread, it looks like the application is blocked until a response is received, which can take a long time.

B) You can make an asynchronous request and wait for the callback . Client code can continue with everything that needs to be done.

 Async_ConvertMovie(params, TheFunctionToCallWhenTheResponseArrives); 

This solution has a big drawback in that the callback function necessarily works in a separate thread. The problem is that getting a response back to the caller is difficult. For example, you clicked a button in a dialog box that called the service asynchronously, but the dialog was closed when the callback returned.

 void TheFunctionToCallWhenTheResponseArrives() { //Difficulty 1: how to get to the dialog instance? //Difficulty 2: how to guarantee in a thread-safe manner that // the dialog instance is still valid? } 

This in itself is not a big problem. However, if you want to make more than one of these calls, and all of them depend on the answer of the previous one, it becomes uncontrollably complicated in my experience.

C) The last parameter that I see is to make an asynchronous request and continue polling until a response arrives. Between the checks received when receiving the answer, you can do something useful. This is the best solution I know to solve a case in which there is a sequence of calls to asynchronous functions. This is because it has a big advantage in that you still have the whole caller context when the answer arrives. In addition, the logical sequence of calls remains reasonably clear. For instance:

 const CallHandle c1 = Sync_ConvertMovie(sourceFile, destFile); while(!c1.ResponseHasArrived()) { //... do something in the meanwhile } if (!c1.IsSuccessful()) return; const CallHandle c2 = Sync_CopyFile(destFile, otherLocation); while(!c1.ResponseHasArrived()) { //... do something in the meanwhile } if (c1.IsSuccessful()) //show a success dialog 

The problem with this third solution is that you cannot return from the caller function. This makes it unusable if the work you want to do between them has nothing to do with the work that you do asynchronously. For a long time, I have been wondering if there is another possibility to call functions asynchronously, and this does not have the drawbacks of the options listed above. Does anyone have an idea, some kind of clever trick?

Note: the above example is a C ++ alias. However, I think this question applies equally to C # and Java, and probably to many other languages.

+4
source share
3 answers

You can consider an explicit “event loop” or “message loop”, not too different from classical approaches, such as a select loop for asynchronous network tasks or a message loop for a window system. Received events can be sent to a callback if necessary, for example, in your example B, but in some cases they can also be tracked in different ways, for example, to complete transactions on a destination machine. FSM is a great way to manage the complexity of protocol interactions that require many steps!

One approach to systematizing these considerations begins with the Reactor template.

Schmidt ACE workload is a good starting point for these problems if you come from C ++ background; Twisted is also quite useful from the Python background; and I’m sure that similar frameworks and sets of summaries exist for, as you say, “many other languages” (the Wikipedia URL I gave points to Reactor implementations for languages ​​other than ACE and Twisted).

+3
source

I tend to go with B, but instead of calling back and forth, I would do all the processing, including the next steps in a separate thread. Nevertheless, the main thread can update the graphical interface and either actively wait for the thread to finish (i.e. display a dialog with a progress bar), or simply let it do its job in the background and receive a notification when it is done. There are currently no problems with complexity, since all processing is actually synchronous in terms of processing flow. From a GUI perspective, this is asynchronous.

Adding to this, .NET has no problem switching to a GUI thread. The BackgroundWorker class and ThreadPool also make this easier (I used ThreadPool, if I remember correctly). For example, in Qt, to stay with C ++ is also quite easy.

I used this approach in our last major application and am very pleased with it.

+2
source

As Alex said, look at Proactor and Reactor as described by Doug Schmidt in Software Architecture Templates.

There are specific implementations for different platforms in ACE.

+1
source

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


All Articles