简体   繁体   中英

How to covert async call to work as sync call in C#

I'm a newbie in C#, and I'm going to develop a small program using a third party network library to send the requests.

Suppose there have some requests (just simple strings) stored in the queue qTasks , and it will handle those requests one by one with the order as submitted, the queue can be updated during execution, and it should be stopped whenever there has error returned.

I can just use a for loop to call the send request command in the array one by one, but unfortunately the sendrequest command is an async method with callback OnStageChanged , and I need to check the result before sending the next request when the status is "Done".

I'm now using the following method to handle it:

In the main UI Thread,

// Put those request text in a queue names qTasks, then call a goNextTask() to process the request one by one.
// The queue can be updated by the UI thread at anytime, goNextTask will be called periodically to handle those pending request in the queue.

private void goNextTask(bool lastSuccess = true)
{
    if (lastSuccess) 
    {
        if (qTasks.Count > 0) 
        {
            // continue to next request
            string requestText = qTasks.Dequeue();
            SendRequest(requestText, OnStageChangeHandler);
        } else {
            // Report for all request sent successfully
        }
    } else {
        // stop and show error
    }
}

The callback method OnStageChangeHandler will be called by the library whenever the stage changes, and it will have state "Done" when completed.

private void OnStageChangeHandler(object sender, StageChangeEventArgs e)
{
    if (e.newState == SessionStates.Done)
    {
        // check result here
        bool success = <...> 

        // then call the goNextTask in UI thread with the result of current request.
        Application.Current.Dispatcher.BeginInvoke(
           System.Windows.Threading.DispatcherPriority.Normal,
          (Action)(() => goNextTask(success)));
    }
}

Although it works fine now, I think it's a little bit stupid as it has a somewhat recursive flow (A -> B -> A -> B ->....).

I learnt that MS has improved the web request handling, so that it can work in sync mode.

I'd like to know if I can have a wrapper to make the above async call work as a sync call, so that it can be done in a simple flow as a loop like that:

while (qTaks.Count > 0) 
{
    if (!sendAndWaitReturn(qTasks.Dequeue())) {
        // Report error and quit
    }
}
// all tasks completed

This sendAndWaitReturn method will send the request, then wait for the status "Done", and then return the result.

I found some example that may use a control flag to indicate the status of the current request, and the callback function will update this control flag, while the UI thread will loop on this flag using a while loop:

while (!requestDone);

so that it will not continue to nextRequest until requestDone . But in this case, the UI will be blocked.

Is there any better way to convert the async call to work as a sync call without blocking the UI thread?

The difficulty you're going to run into is you have conflicting desires. On one hand, you want to avoid blocking the UI thread. On the other hand, you don't want to run things asynchronously and so you're going to block the UI thread.

You're going to have to pick one, and there's absolutely no reason to keep on doing things synchronously (especially in light of blocking the UI thread). If it hurts when you do that, don't do that.

You haven't specified, but I'm guessing that you're starting this processing from a button click event. Make the method invoked by that click event async. For example:

private async void StartProcessing_Click(object sender, EventArgs e)
{
  await Task.Run(() => StartProcessing());
}

There, you've started processing and the UI thread isn't tied up.

The next thing is that, you're right, having the event behave in that cyclical manner is silly. The event is to notify someone that the state has changed, its goal isn't to manage queue policy. The queue should manage queue policy (or if you'd rather not abstract that out, the method that processes requests ).

So how would you do that? Well, you've said that SendRequest hands the session object back to the caller. The caller is presumably the one who is orchestrating queue policy and determining whether or not to call SendRequest again.

Have the caller check the session object for validity and decide whether to keep going based on that.

Additionally, I'm unfamiliar with that particular library, but briefly glancing at the documentation it looks like there's also a SendRequestAndWait() method with the same signature and that sounds like it might better meet your needs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM