【问题标题】:Task.Factory.StartNew guaranteed execution sequenceTask.Factory.StartNew 保证执行顺序
【发布时间】:2016-04-29 13:26:06
【问题描述】:

我有 2 个线程调用“Task.Factory.StartNew”。假设一个线程 (ThreadA) 比另一个线程 (ThreadB) 稍微领先。

...在线程 A 中,稍微提前调用

Task.Run(() => {
  SomeMethodInThreadA();
});

...在线程 B 中,稍后调用

Task.Run(() => {
  SomeMethodInThreadB();
});

TaskScheduler 是否保证 SomeMethodInThreadASomeMethodInThreadB 之前先执行?

如果没有,我该怎么做?使用 Task.Factory.StartNew 并传入特殊的 TaskScheduler?

另外,除了保证 SomeMethodInThreadA 先被处理外,我还想确保 SomeMethodInThreadA 在执行 SomeMethodInThreadB 之前先完成。

我已经研究过使用StaTaskScheduler,但我不确定这是否是解决方案。

编辑:

没有给出太多关于我继承的程序的细节/戏剧,我想我正在寻找的是一个自定义的 TaskScheduler:

  1. 在代表排队时遵守序列

  2. 串行执行

  3. 在同一个线程中执行,类似于我上面链接的 StaTaskScheduler 源代码

【问题讨论】:

  • 不要使用 StartNew blog.stephencleary.com/2013/08/startnew-is-dangerous.html 除非您确实使用了不同的调度程序(我可能不会推荐)
  • 好吧,我不得不这样做,因为我不知何故需要一个特殊的 TaskScheduler,类似于 StaTaskScheduler 正在尝试做的事情。
  • 我觉得你在解决错误问题。
  • 如果有保证,那么所有任务都将在全球范围内序列化。这与只有一个线程运行任务相同。
  • { SomeMethodInThreadA(); SomeMethodInThreadB(); } 怎么样?

标签: c# multithreading task-parallel-library task


【解决方案1】:

没有透露太多关于节目的细节/戏剧,我 继承,我想我正在寻找的是一个自定义 TaskScheduler 其中:

  1. 在代表排队时遵守序列

  2. 串行执行

  3. 在同一个线程中执行,类似于我上面链接的 StaTaskScheduler 源代码

如果这就是你想要的,你需要做的就是创建一个BlockingCollection<Action>,然后在一个专用线程上循环遍历列表。

public class BackgroundJobSchedueller
{
    readonly BlockingCollection<Action> _queue;
    readonly Thread _thread;

    public BackgroundJobSchedueller()
    {
        _queue = new BlockingCollection<Action>()
        _thread = new Thread(WorkThread)
        {
            IsBackground = true,
            Name = "Background Queue Processor"
        };
        _thread.Start();
    }

    public void StopSchedueller()
    {
        //Tell GetConsumingEnumerable() to let the user out of the foreach loop
        // once the collection is empty.
        _queue.CompleteAdding();

        //Wait for the foreach loop to finish processing.
        _thread.Join();
    }

    public void QueueJob(Action job)
    {
        _queue.Add(job);
    }

    void WorkThread()
    {
        foreach(var action in _queue.GetConsumingEnumerable())
        {
            try
            {
                action();
            }
            catch
            {
                //Do something with the exception here
            }
        }
    }
}

当没有工作要做时,线程休眠,在 foreach 上被阻塞,一旦你将一个项目添加到队列中,它就会被处理然后重新进入休眠状态。

【讨论】:

  • 天哪,这行得通!尤其是我的代表也有相同的签名!
  • 即使您的方法没有相同的签名,您仍然可以通过将方法包装在 Lambada schedueller.QueueJob(() =&gt; SomeMethodWithParams(42)) 中来使用它
【解决方案2】:

TaskScheduler 是否保证 SomeMethodInThreadA 先于 SomeMethodInThreadB 执行?

没有。

如果没有,我该怎么做?

您是否使用ContinueWith 继续您的第一个任务

类似:

var task = Task.Run(() => {
    SomeMethodInThreadA();
});

然后:

task.ContinueWith(t => {
    SomeOtherMethodInThreadA();
});

【讨论】:

  • 这两个线程不一定相互通信,所以我无法掌握那个“任务”变量。
  • @alpinescrambler:当然可以。加上它存在于父线程中而不是线程 A 中。
  • 我的意思是,这两个线程的编写方式是相互独立的。无法传递您在示例中捕获的“任务”变量。
  • @alpinescrambler 然后你需要解决这个问题。一项任务不能依赖于它不允许知道的任何事情。您需要设计您的程序,以便具有依赖项的操作接受 Task 作为这些行的延续或类似的东西。
  • @alpinescrambler:您不必将任何内容从一个线程传递到另一个线程。启动第一个任务的父级是 task 所在的位置。然后在同一父线程中添加延续。线程 A 不需要知道关于任务的任何事情(事实上,没有线程 B)。如果这两个任务是用不同的方法创建的,以至于task 超出了范围,那么只需将其粘贴在成员字段中即可。
【解决方案3】:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Task.Factory.StartNew(() => MethodA())
                .ContinueWith(a =>
                {
                     MethodB();
                });

            Console.WriteLine("Press a key to exit");
            Console.ReadKey(false);

        }

        public static void MethodA()
        {
            Thread.Sleep(2000);
            Console.WriteLine("Hello From method A {0}", Thread.CurrentThread.ManagedThreadId);
        }

        public static void MethodB()
        {
            Thread.Sleep(1000);
            Console.WriteLine("Hello From method B {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }
}

【讨论】:

    【解决方案4】:

    TaskScheduler 是否保证 SomeMethodInThreadA 得到 在 SomeMethodInThreadB 之前先执行?

    当然不是。

    如果没有,我该怎么做?

    如果 2 个线程完全相互独立,但 SomeMethodInThreadB 必须在 SomeMethodInThreadA 完成后执行,您可以使用标准线程同步例程:

    public static AutoResetEvent Wait = new AutoResetEvent(false);
    

    然后在开始第一个任务时:

    var task = Task.Run(() => 
    {
        // Sets the state of the event to nonsignaled, causing threads to block.
        Wait.Reset();
    
        // Execute the first task
        SomeMethodInThreadA();
    
        // The first task has finished executing -> signal the second
        // thread which is probably waiting that it can start processing
        // the second task
        Wait.Set();
    });
    

    然后在第二个线程中等待第一个方法已完成执行的信号:

    Task.Run(() => 
    {
        if (!Wait.WaitOne(TimeSpan.FromMinutes(30)))
        {
            // We have waited for too long the first task to execute - giving up
        }
        else
        {
            // The first task has finished executing - we can now
            // proceed with the second task
            SomeMethodInThreadB();
        }
    });
    

    显然,您可以定义第二个线程在放弃前等待第一个任务完成的时间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-07
      • 2018-01-07
      • 1970-01-01
      • 2014-07-22
      • 1970-01-01
      • 2016-05-02
      • 2013-11-25
      • 2011-01-13
      相关资源
      最近更新 更多