简体   繁体   English

如何隐藏异步调用以在C#中用作同步调用

[英]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. 我是C#的新手,我将使用第三方网络库开发一个小程序来发送请求。

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. 假设队列qTasks存储了一些请求(只是简单的字符串),它将按照提交的顺序逐个处理这些请求,可以在执行期间更新队列,并且只要返回错误,就应该停止该队列。

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". 我只能使用for循环在数组中一个接一个地调用send request命令,但是不幸的是sendrequest命令是一个带有回调OnStageChanged的异步方法,并且当状态为“时,我需要检查结果,然后发送下一个请求完成”。

I'm now using the following method to handle it: 我现在使用以下方法来处理它:

In the main UI Thread, 在主UI线程中,

// 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. 每当阶段更改时,库都将调用回调方法OnStageChangeHandler ,并且完成时其状态将为“完成”。

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 ->....). 尽管它现在可以正常工作,但我认为它有点愚蠢,因为它具有一定的递归流程(A-> B-> A-> B-> ....)。

I learnt that MS has improved the web request handling, so that it can work in sync mode. 我了解到MS已改进了Web请求处理,因此它可以在同步模式下工作。

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. sendAndWaitReturn方法将发送请求,然后等待状态“完成”,然后返回结果。

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: 我发现了一些示例,该示例可能使用控制标志来指示当前请求的状态,并且回调函数将更新此控制标志,而UI线程将使用while循环在此标志上循环:

while (!requestDone);

so that it will not continue to nextRequest until requestDone . 这样它就不会继续到nextRequest直到requestDone为止。 But in this case, the UI will be blocked. 但是在这种情况下,UI将被阻止。

Is there any better way to convert the async call to work as a sync call without blocking the UI thread? 有没有更好的方法可以将异步调用转换为同步调用而不阻塞UI线程?

The difficulty you're going to run into is you have conflicting desires. 您将遇到的困难是您有相互矛盾的欲望。 On one hand, you want to avoid blocking the UI thread. 一方面,您要避免阻塞UI线程。 On the other hand, you don't want to run things asynchronously and so you're going to block the UI thread. 另一方面,您不想异步运行事物,因此将阻塞UI线程。

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). 您将不得不选择一个,并且绝对没有理由继续进行同步处理(尤其是考虑到阻塞UI线程)。 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. 使该click事件调用的方法异步。 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. 在那里,您已经开始处理,并且UI线程没有被占用。

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. 好吧,您已经说过SendRequest将会话对象移交给调用者。 The caller is presumably the one who is orchestrating queue policy and determining whether or not to call SendRequest again. 调用者大概是协调队列策略并确定是否再次调用SendRequest的那个。

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. 另外,我不熟悉该特定库,但是简要浏览一下文档,看起来好像还有一个具有相同签名的SendRequestAndWait()方法,这听起来可能会更好地满足您的需求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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