【问题标题】:Windows 8 Thread improvements - alternative to Task.Run()?Windows 8 线程改进 - 替代 Task.Run()?
【发布时间】:2013-11-12 06:04:40
【问题描述】:

我继承了一个 C#/XAML/Win 8 应用程序。有一些代码设置为每 n 秒运行一次。

设置它的代码是:

if(!_syncThreadStarted)
{
    await Task.Run(() => SyncToDatabase());
    _syncThreadStarted = true;
}

上面的代码运行一次。

然后在 SyncToDatabase() 我们有:

while (true)
{
    DatabaseSyncer dbSyncer = new DatabaseSyncer();
    await dbSyncer.DeserializeAndUpdate();

    await Task.Delay(10); // after elapsed time re-run above code
}

DeserializeAndUpdate 方法查询内存中的对象集合并将这些对象推送到 Web 服务。

有时向 Web 服务发送请求所需的时间比预期的要长,这意味着发送了重复的项目。

问题:有没有办法让我可以在 SyncToDatabase() 方法中停止/中止/销毁线程或某种类型的线程池/后台工作程序,然后在完成后初始化/启动它?这将确保在前一个请求仍处于待处理状态时不会触发后续请求。

编辑:我对线程不是很了解,但我想要的逻辑是:

创建每 x 秒运行某个方法的线程,当它启动该线程时停止“每 x 秒运行”部分,在线程完成后再次启动“每 x 秒运行”部分。

例如如果线程在上午 10:01:30 开始并且直到上午 10:01:39(9 秒)才完成,下一个线程应该在上午 10:01:44(工作完成后 5 秒)开始 - 这有意义吗?我不希望同时运行 2 个或更多线程。

这是我上面的代码:

var period = TimeSpan.FromSeconds(5);
var completed = true;
ThreadPoolTimer syncTimer = ThreadPoolTimer.CreatePeriodicTimer(async (source) =>
{
    // stop further threads from starting (in case this work takes longer than var period)
    syncTimer.Cancel();
    DatabaseSyncer dbSyncer = new DatabaseSyncer();
    await dbSyncer.DeserializeAndUpdate(); // makes webservices calls

    Dispatcher.RunAsync(CoreDispatcerPriority.High, async () =>
    {
        // Update UI
    }

    completed = true;
}, period,
(source) =>
{
    if(!completed)
    {
        syncTimer.Cancel(); // not sure if this is correct...
    }
}

谢谢, 安德鲁)

【问题讨论】:

    标签: c# multithreading


    【解决方案1】:

    这并不特定于 Windows 8。通常Task.Run 用于 CPU 密集型工作,将其卸载到池线程并保持 UI(或核心服务循环)响应。在您的情况下,据我所知,主要有效负载是 dbSyncer.DeserializeAndUpdate,它已经是异步的,并且很可能是网络 IO 绑定的,而不是 CPU 绑定的。

    另外,原代码作者在await Task.Run(() => SyncToDatabase())之后做了_syncThreadStarted = true。这没有任何意义,因为在执行_syncThreadStarted = true 时,池线程上的工作已经完成,这要感谢await

    要取消SyncToDatabase 内的循环,您可以使用Task Cancellation PatternSyncToDatabase 本身是 async 方法吗?我想是的,因为while loop 中有一个await。鉴于此,调用它的代码可能如下所示:

    if(_syncTask != null && !_syncTask.IsCompleted)
    {
        _ct.Cancel();
        // here you may want to make sure that the pending task has been fully shut down, 
        // keeping possible re-entrancy in mind
        // See: https://stackoverflow.com/questions/18999827/a-pattern-for-self-cancelling-and-restarting-task
        _syncTask = null;
    }
    _ct = new CancellationTokenSource();
    // _syncTask = SyncToDatabase(ct.Token); // do not await
    // edited to run on another thread, as requested by the OP
    var _syncTask = Task.Run(async () => await SyncToDatabase(ct.Token), ct.Token);
    _syncThreadStarted = true;
    

    SyncToDatabase 可能看起来像:

    async Task SyncToDatabase(CancellationToken token)
    {
        while (true)
        {
            token.ThrowIfCancellationRequested();
            DatabaseSyncer dbSyncer = new DatabaseSyncer();
            await dbSyncer.DeserializeAndUpdate();
            await Task.Delay(10, token); // after elapsed time re-run above code
        }
    }
    

    查看this answer 了解有关如何取消和重新启动任务的更多详细信息。

    【讨论】:

    • 感谢您的回答。所以你答案的第一部分是注册任务?在 SyncToDatabase 运行时,它似乎并没有阻止任务发生,除非我误解了。
    • @andrewb、_syncTask_ct 是您班级的字段。在使用_syncTask = SyncToDatabase(ct.Token) 开始新任务之前检查它们并首先取消任务的旧实例。因此,只有一个任务实例处于活动状态。
    • 您的代码是否创建了单独的线程?如果可能的话,我需要 SyncToDatabase 在单独的非 UI 线程中运行。
    • @andrewb,你确定你需要另一个线程吗?请参阅我关于 CPU 和 IO 绑定任务的注释。无论如何,我已经更新了代码以在另一个线程上运行它。确保您了解嵌套任务的工作原理:blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
    • 我需要 UI 线程保持响应。请参阅我更新的原始帖子,以更清楚地解释我的需求。
    【解决方案2】:

    我可能误解了这个问题,但是SynchToDatabase() 的执行将等待await dbSyncer.DeserializeAndUpdaet() 的完成(由于await 关键字,请看图;))然后再执行继续,然后延迟10 毫秒(你想要 10 毫秒还是你的意思是 10 秒?Task.Delay 的参数以毫秒为单位),然后循环返回以重新执行 DbSyncer 方法,所以我没有看到问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-16
      • 2012-11-30
      • 1970-01-01
      相关资源
      最近更新 更多