简体   繁体   English

c#在执行之前建立一个任务列表

[英]c# build a list of tasks before executing

i'm trying to build a list of tasks before executing them.我正在尝试在执行任务之前建立一个任务列表。 here's some example code:这是一些示例代码:

    public string Returnastring(string b)
    {
        return b;
    }

    public string Returnanotherstring(string a)
    {
        return a;
    }


    private void btnT_Click(object sender, EventArgs e)
    {
        bool cont = true;

        var Returnastringtask = Task.Factory.StartNew(() => Returnastring("hi"));
        var Returnanotherstringtask = Task.Factory.StartNew(() => Returnanotherstring("bye"));

        if (cont)
        {
            Task.WaitAll(new Task[] { Returnastringtask });
        }
        else
        {
            Task.WaitAll(new Task[] { Returnanotherstringtask });
        }

i know this code doesn't behave how i expect it as both tasks run.我知道当这两个任务都运行时,这段代码的行为并不符合我的预期。 i want to basically create the tasks initially and then execute one or the other based on the bool.我想基本上最初创建任务,然后根据布尔值执行一个或另一个。 i don't want to create the tasks inside the true or false conditions as i want to avoid code copying.我不想在 true 或 false 条件下创建任务,因为我想避免代码复制。 by this i mean if cont is true i might want to run tasks 1,2,3,4 but if cont is false i might want to run tasks 2,3,7,8.我的意思是,如果 cont 为真,我可能想运行任务 1、2、3、4,但如果 cont 为假,我可能想运行任务 2、3、7、8。

Well, another approach, (which I find very direct)嗯,另一种方法,(我觉得很直接)

        var list = new List<Task>();
        for (var i = 0; i < 10; ++i)
        {
            var i2 = i;
            var t = new Task(() =>
                {
                    Thread.Sleep(100);
                    Console.WriteLine(i2);
                });
            list.Add(t);
            t.Start();
        }

        Task.WaitAll(list.ToArray());

而不是使用Task.Factory.StartNew来创建任务(线索在名称中),而是通过使用new Task(...)与您的 lambdas 来创建它们,然后只需在您的条件中使用taskName.Start()想开始他们。

You can create an array of Action based on a flag, and then use Parallel.Invoke() to run in parallel all the actions in the array and wait for them to finish.您可以根据一个标志创建一个Action数组,然后使用Parallel.Invoke()运行该数组中的所有操作并等待它们完成。

You can use lambdas for the actions which will allow you to assign their return values to a local variable, if you want.如果需要,您可以将 lambda 用于操作,这将允许您将它们的返回值分配给局部变量。

Here's a complete compilable example.这是一个完整的可编译示例。 Try it with getFlag() returning true and again with it returning false :尝试使用getFlag()返回true并再次返回false

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    sealed class Program
    {
        void run()
        {
            bool flag = getFlag();
            var results = new string[5];
            Action[] actions;

            if (flag)
            {
                actions = new Action[]
                {
                    () => results[0] = function("f1"),
                    () => results[1] = function("f2"),
                    () => results[2] = function("f3")
                };
            }
            else
            {
                actions = new Action[]
                {
                    () => results[3] = function("f4"),
                    () => results[4] = function("f5")
                };
            }

            Parallel.Invoke(actions); // No tasks are run until you call this.

            for (int i = 0; i < results.Length; ++i)
                Console.WriteLine("Result {0} = {1}", i, results[i]);
        }

        private bool getFlag()
        {
            return true; // Also try with this returning false.
        }

        string function(string param)
        {
            Thread.Sleep(100); // Simulate work.
            return param;
        }

        static void Main(string[] args)
        {
            new Program().run();
        }
    }
}

The Task.Factory.StartNew will actually begin your tasks. Task.Factory.StartNew 将实际开始您的任务。 You want to setup the tasks and then run them based on some logic.您想设置任务,然后根据某些逻辑运行它们。

You can build your tasks wherever but you should start them after the logic.你可以在任何地方构建你的任务,但你应该在逻辑之后开始它们。 This example builds them after the logic.此示例在逻辑之后构建它们。

Maybe you could run it like this:也许你可以这样运行它:

If(A)
{
     doA();
}
Else
{
     doB();
}

Then start your tasks inside the function you call like:然后在您调用的函数内开始您的任务,例如:

public void doA()
{
     for (int i = 0; i < NumberOfTasks; i++)
     {
          tasks[i] = Task.Factory.StartNew(() =>
          {
               try
               {
                    //enter tasks here 
                    // i.e. task 1, 2, 3, 4
               }
          }
     }, token);

     Task.WaitAll(tasks);    
}

I based what I did on what Samuel did, except I have a recursive event handler that needs to finish what it's doing because its child events depend on it having completed (for nesting controls in a dynamic UI in an ASP.NET app).我所做的一切都是基于 Samuel 所做的,除了我有一个递归事件处理程序需要完成它正在做的事情,因为它的子事件依赖于它已经完成(用于在 ASP.NET 应用程序的动态 UI 中嵌套控件)。 So if you want to do the same thing, except you're handling an event, and you are NOT multithreading because you need to process multiple tasks synchronously without goofing around with your call stack.所以如果你想做同样的事情,除了你正在处理一个事件,而且你不是多线程,因为你需要同步处理多个任务,而不是在调用堆栈上乱搞。

    private static Queue<Task> _dqEvents = new Queue<Task>();
    private static bool _handlingDqEvent = false;

    protected void HandleDynamicQuestion(int SourceQuestionId, int QuestionId)
    {
        //create a task so that we can handle our events in sequential order, since additional events may fire before this task is completed, and depend upon the completion of prior events
        Task task = new Task(() => DoDynamicQuestion(SourceQuestionId, QuestionId));
        lock(_dqEvents) _dqEvents.Enqueue(task);
        if (!_handlingDqEvent)
        {
            try
            {
                //lockout any other calls in the stack from hitting this chunk of code
                lock (_dqEvents) _handlingDqEvent = true;

                //now run all events in the queue, including any added deeper in the call stack that were added to this queue before we finished this iteration of the loop
                while (_dqEvents.Any())
                {
                    Task qt;
                    lock (_dqEvents) qt = _dqEvents.Dequeue();
                    qt.RunSynchronously();
                }
            }
            finally
            {
                lock (_dqEvents) _handlingDqEvent = false;
            }
        }
        else
            //We exit the method if we're already handling an event, as the addition of new tasks to the static queue will be handled synchronously.
            //Basically, this lets us escape the call stack without processing the event until we're ready, since the handling of the grandchild event 
            //is dependent upon its parent completing.
            return;
    }

    private void DoDynamicQuestion(int SourceQuestionId, int QuestionId)
    {
        //does some stuff that has no dependency on synchronicity

        //does some stuff that may eventually raise the event above

        //does some other stuff that has to complete before events it triggers can process correctly
    }

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

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