如何追踪异步/可等待任务是否在运行

15

我正在尝试从基于事件的异步模式转换,其中我使用唯一的 id 和 asynoperationmanager 跟踪运行方法。
由于 Windows 8 应用程序中已经放弃了这种模式,我正在尝试使用 Async/Await 实现类似的效果,但是我还无法想出具体的解决方案。
我想要实现的效果是类似于

private async Task updateSomething()
{
    if(***the method is already running***)
    {
        runagain = true;
    }
    else
    {
        await someMethod();
        if (runagain)
        {
            run the method again
        }            
    }
}

我遇到的问题是如何判断方法是否正在运行。我尝试创建一个任务并查看它的状态以及异步方法的.status,但它们似乎不是正确的查找位置。 谢谢

更新:这是我在.net 4中使用的代码,以实现相同的结果。_updateMetaDataAsync是基于事件的异步模式。

private void updateMetaData()
    {
        if (_updateMetaDataAsync.IsTaskRunning(_updateMetaDataGuid_CheckAllFiles))
        {
            _updateMetaDataGuid_CheckAllFiles_Again = true;
        }
        else
        {
            _updateMetaDataGuid_CheckAllFiles_Again = false;
            _updateMetaDataAsync.UpdateMetaDataAsync(_updateMetaDataGuid_CheckAllFiles);
        }
    }

private void updateMetaDataCompleted(object sender, UpdateMetaDataCompletedEventArgs e)
    {
        if (_updateMetaDataGuid_CheckAllFiles_Again)
        {
            updateMetaData();
        }
    }
3个回答

9
async/await 被设计用于创建在 UI 线程之外异步执行的顺序操作。虽然可以让它执行并行操作,但通常这些操作会“连接”回 UI 线程并返回某种结果。(也有可能使用 await 执行“执行后即忘”的类型异步操作,但不建议这样做)。也就是说,async/await 本身没有固有的支持进度报告的功能。
您可以使用 async/await 从代码中获取进度,但需要使用新的进度接口,如 IProgress<T>。有关使用 async/await 进行进度报告的更多信息,请参见http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx。迁移到这个新接口只需要调用一个 IProgress 委托,而不是一个 Progress 事件。

我不是出于选择而不使用Async/Await,更多的是因为微软在Windows API的.net中滥用了Async标记方法。有没有任何方法可以避免走异步/等待的路线? - Oli
4
一个方法名以Async结尾并不意味着你必须使用await。你可以直接使用返回的Task对象,然后对它进行任何你想要的操作,比如使用ContinueWith方法。例如: webClient.DownloadStringAsync(myUri).ContinueWith(t=>Trace.WriteLine("done"));,这里就没有使用await - Peter Ritchie

4
如果您正在使用自己创建的Task,您可以检查该任务的Status属性(或者只查看Task.IsCompleted如果完成是您唯一感兴趣的状态)。
话虽如此,await将不会在操作完成、引发异常或取消之前“返回”。基本上,您可以安全地假设,如果您仍在等待“await”,则您的任务尚未完成。

谢谢Reed,你能否贴一些代码?这正是我一直在尝试做的事情,但状态始终显示为"WaitingForActivation",尽管我知道任务正在运行。我基本上想要声明一个任务(Task),然后在构造函数中将其分配给一个方法。从另一个类的方法中,我可以启动它并检查它的状态,如果有更好的方法,请随意指出。 - Oli
@Oli 通常情况下,拥有一个尚未启动的任务是一个不好的主意 - TPL和async/await的设计理念是任务始终处于“活跃”状态,这就是为什么更倾向于使用Task.Run而不是new Task。你实际上的目标是什么?(不是机制,而是你想要实现的目标) - Reed Copsey
我基本上有一个长时间运行的任务,它从互联网下载信息并更新应用程序中的对象数据。当新对象出现并请求运行任务时,应用程序会得到更新。我的 .net 4 逻辑基于事件模式的类,并声明了该类的实例(我们称之为 methodasync)。因此,当检测到新对象时,它调用 methodasync.IsTaskRunning(taskID)。如果没有运行,则运行该任务,但如果正在运行,则标记一个布尔值,表示需要再次运行。当 methodasync 完成时,它检查该布尔值,如果为“True”,则再次运行。 - Oli
@Oli,将委托的队列排成计划表,然后运行队列中的项目怎么样?这似乎比尝试“重新运行”相同操作要清晰得多。 - Reed Copsey
关于队列的内容请参见下文;-) 尽管同样的方法被重新运行,但每次都在不同的对象上工作。如果一次性有1000个对象进来,我不想让1000个任务排队等待。下一次处理将会处理这1000个对象并完成工作,但是我会留下999个无意义的任务需要运行。为了更清晰地表达,我已经更新了我的问题并附上了当前使用的代码。 - Oli
@Oli 看起来你可以通过使用带有布尔值的 while 循环,并使用 async/await 来处理这个问题... - Reed Copsey

0
SemaphoreSlim queueToAccessQueue = new SemaphoreSlim(1);
object queueLock = new object();
long queuedRequests = 0;
Task _loadingTask;
public void RetrieveItems() {
  lock (queueLock) {
      queuedRequests++;
      if (queuedRequests == 1) { // 1 is the minimum size of the queue before another instance is queued
        _loadingTask = _loadingTask?.ContinueWith(async () => {
          RunTheMethodAgain();
          await queueToAccessQueue.WaitAsync();
          queuedRequests = 0; // indicates that the queue has been cleared;
          queueToAccessQueue.Release()
        }) ?? Task.Run(async () => {
          RunTheMethodAgain();
          await queueToAccessQueue.WaitAsync();
          queuedRequests = 0; // indicates that the queue has been cleared;
          queueToAccessQueue.Release();
        });
      }
  }
}
public void RunTheMethodAgain() {
  ** run the method again **
}

"额外的好处是,您可以看到队列中有多少项!"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接