【问题标题】:Wait for the end of an asynchronous task等待异步任务结束
【发布时间】:2020-04-16 11:41:06
【问题描述】:

我有一个异步运行的任务,然后我有第二种方法,它需要从异步任务中检索信息。 我无法修改异步任务,所以我想知道是否可以告诉第二种方法等到异步方法完成。

foreach (AgentModel ag in Agents)
{
    if (ag.IsEnabledRow == true)
    {
        if (ag.IsSelected == true)
        {
            if (ag.selectedMatrice != null)
            {
                if (ag.selectedWeeks != null)
                {
                    //on vérifie le cycle choisi et on adapte la date en conséquence
                    semaineAAppliquer(ag);

                    ag.IsEnabledRow = false;

                    Task<int> attribuerPlanning = Gestion.AttrPlanning(
                        _dataService.ParamGlobaux.IDEtablissement,
                        _dataService.ParamGlobaux.Matricule,
                        _dataService.ParamGlobaux.ConnectionString,
                        ag.Matricule, ag.selectedMatrice.IDMatrice, DsCalendrierCongés,
                        dateDebutCycle, ag.dateFin, HoraireAZero, CompleterPriseVide,
                        RemplacerRH, JFRepos,
                        (text, title, buttons) => System.Windows.MessageBox.Show(
                            text, title, buttons), _progress, DecalageSemaine,
                            appliquerCouleur, _ToutEtablissement);
                }
            }
            else
            {
                System.Windows.MessageBox.Show($"Sélectionner une matrice pour" +
                    $" l'agent {ag.Nom}.");
            }
        }
    }
}

UpdateListeContrats();

attribuerPlanning 方法是异步方法,我希望在不修改方法本身的情况下,它在调用UpdateListeContrats 之前结束。

或者说UpdateListeContrats,在其他方法完成之前不要触发自己。

(目前,updateListeContrats 在没有通过attributerPlanning 方法更新信息的情况下启动。)

【问题讨论】:

  • 只是把等待任务属性规划。如果 updateListeContrats 超出当前方法的范围,您可以 .Result for attributePlanning

标签: c# asynchronous task


【解决方案1】:

首先,您实际上不应该“等待”(同步)任务,但“等待”它(异步)是可以的。

在这种情况下,最合适的位置可能是:在呼叫站点,即

if (ag.selectedWeeks != null)
{
    //on vérifie le cycle choisi et on adapte la date en conséquence
    semaineAAppliquer(ag);

    ag.IsEnabledRow = false;

    var result = await Gestion.AttrPlanning( ... );
}

这样除了简单之外,还避免了并发问题;大多数代码都没有预料到同一上下文中的并发使用。

在一般情况下,您可以在一个地方捕获任务(等),然后等待它,但在您的情况中,问题变成了如何处理foreach/if 等;如果您同时运行它们,可能会有零个、一个或多个这样的任务。但我想你可以把它们放在一个列表中,即

var pending = new List<Task<int>>();
// ...
            pending.Add(attribuerPlanning);
// ...
var results = await Task.WhenAll(pending);

【讨论】:

  • 另外,如果他不想让 te 方法异步(顺便说一句,这是一个很好的做法),他可以使用 .Result,我猜是这样 :)
  • @EmilianoJavierGonzález 访问 .Result不是一个好主意;这就是“同步而不是异步”,它可能会积极有害(它本质上与.Wait() 相同)
  • 是的,我知道 :) 我只是说使用异步,这是正确的方法,会迫使他使他的方法异步 :)
【解决方案2】:

更新:正如 Marc 指出的那样,如果您有一些不应该或不应该在主线程中阻塞的东西,例如 GUI,请不要这样做。

调用后可以attribuerPlanning.Wait();

或者,如果您想在 foreach 异步中运行所有任务并等待所有任务在 UpdateListeContrats 之前完成,您可以在 foreach 之外创建一个任务列表:

List<Task> tasks = new List<Task>();

并在循环体中将当前任务添加到列表中:

var task = Task.Run(() => Gestion.AttrPlanning(...) );
tasks.Add(task);

在foreach之外做

Task.WhenAll(tasks).Wait();
UpdateListeContrats();

【讨论】:

  • .Wait()(和.Result)是(是)“异步同步”,这在许多情况下是非常危险的——即使远程可能也应该避免它;这通常表明您正在将异步(或同步,取决于您的观点)硬塞到它不适合的地方
  • ".Wait()(和 .Result)是(是)“异步同步”,这在许多情况下非常危险“在许多情况下,锤子也可能很危险 :) 但是在这种情况下你是对的,我刚刚再次检查了他的代码,似乎有一些 GUI 内容不应该被 Wait() 阻止。我更新了我的答案,不要这样做。
  • @JohnathanBarclay 外部任务将在内部完成时完成。它对我有用,还是我错过了什么?
  • @jojess 不,你在这方面是正确的; Task.Run 有一个接受 Func&lt;Task&gt; 的重载,它返回内部 Task 而不是包装它。当别无选择只能从同步上下文调用异步代码时,Task.RunTask.Wait 只不过是变通方法,这并没有逃避这一事实。两者都有其负面影响,例如Task.Wait 可能会导致死锁,但您没有提及这一点。如果可能,正确的解决方案是按照 Marc 的回答等待任务。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-18
  • 1970-01-01
  • 1970-01-01
  • 2014-09-06
  • 2013-02-10
  • 2017-07-22
  • 1970-01-01
相关资源
最近更新 更多