c# build a list of tasks before executing

六眼飞鱼酱① 提交于 2020-12-30 05:29:52

问题


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. 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.


回答1:


Instead of using Task.Factory.StartNew to create the tasks (the clue is in the name), instead just create them by using new Task(...) with your lambdas, then simply use taskName.Start() inside the condition you want to begin them.




回答2:


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());



回答3:


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.

You can use lambdas for the actions which will allow you to assign their return values to a local variable, if you want.

Here's a complete compilable example. Try it with getFlag() returning true and again with it returning 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();
        }
    }
}



回答4:


The Task.Factory.StartNew will actually begin your tasks. 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);    
}



回答5:


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). 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
    }


来源:https://stackoverflow.com/questions/22377533/c-sharp-build-a-list-of-tasks-before-executing

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!