C# Task任務佇列

2021-06-05 08:00:13

新建一個Winfrom專案,加入下面程式碼:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 執行緒2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Task task1 = new Task(() =>
            {
                Thread.Sleep(400);
                Console.WriteLine("task1");
            });
            Task task2 = new Task(() =>
            {
                Thread.Sleep(300);
                Console.WriteLine("task2");
            });
            Task task3 = new Task(() =>
            {
                Thread.Sleep(200);
                Console.WriteLine("task3");
            });
            Task task4 = new Task(() =>
            {
                Thread.Sleep(100);
                Console.WriteLine("task4");
            });
            task1.Start();
            task2.Start();
            task3.Start();
            task4.Start();
        }
    }
}

執行:

由於延時不同,最先執行的Task1,反而最後一個執行完,那麼如果要求從任務1,一直執行到任務4,怎麼寫呢?

程式碼:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 執行緒2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private List<Task> TaskList = new List<Task>();

        private void Form1_Load(object sender, EventArgs e)
        {
            Task task1 = new Task(() =>
            {
                Thread.Sleep(400);
                Console.WriteLine("task1");
            });
            Task task2 = new Task(() =>
            {
                Thread.Sleep(300);
                Console.WriteLine("task2");
            });
            Task task3 = new Task(() =>
            {
                Thread.Sleep(200);
                Console.WriteLine("task3");
            });
            Task task4 = new Task(() =>
            {
                Thread.Sleep(100);
                Console.WriteLine("task4");
            });

            TaskList.Add(task1);
            TaskList.Add(task2);
            TaskList.Add(task3);
            TaskList.Add(task4);

            foreach (Task task in TaskList)
            {
                task.Start();
                task.Wait();
            }
        }
    }
}

執行:

用上面的方法雖然有效,但會阻塞主執行緒,導致winfrom介面卡住,無法操作,下面就用非同步的方法解決問題

程式碼:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 執行緒2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        private List<Task> TaskList = new List<Task>();

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void Button_Calculate_Click(object sender, EventArgs e)
        {
            Task task1 = new Task(async () =>
            {
                await Task.Delay(TimeSpan.FromSeconds(4));
                Console.WriteLine("task1");
            });
            Task task2 = new Task(async () =>
            {
                await Task.Delay(TimeSpan.FromSeconds(3));
                Console.WriteLine("task2");
            });
            Task task3 = new Task(async () =>
            {
                await Task.Delay(TimeSpan.FromSeconds(2));
                Console.WriteLine("task3");
            });
            Task task4 = new Task(async () =>
            {
                await Task.Delay(TimeSpan.FromSeconds(1));
                Console.WriteLine("task4");
            });

            TaskList.Add(task1);
            TaskList.Add(task2);
            TaskList.Add(task3);
            TaskList.Add(task4);

            foreach (Task task in TaskList)
            {
                task.Start();
                task.Wait();
            }
        }
    }
}

執行:

用非同步方式雖然介面不會卡住,但另一個問題來了,task.wait()方法似乎沒有效果。裡面的任務佇列依然沒有按順序來執行。

經過一段時間的研究,後面我終於找到方法了,用下面的方法既不會卡住UI,也會按照佇列來執行,雖然寫法不是特別好,讀者可以按下面方法自己封裝了。

private void Test()
{
    Task.Run(() =>
    {
        Task t1 = new Task(() => {
            Thread.Sleep(2000);
            Console.WriteLine("t1");
            num = 1;
        });
        t1.Start();
        t1.Wait();
        Task t2 = new Task(() => {
            Thread.Sleep(1000);
            Console.WriteLine("t2");
            num = 3;
        });
        t2.Start();
        t2.Wait();
        Console.WriteLine("執行緒執行完畢");
    });
}

執行:

也可以這樣寫:

private async void Test()
{
    await Task.Run(async () =>
    {
        await Task.Delay(4000);
        Trace.WriteLine("第1個執行緒執行");
    });
    await Task.Run(async () =>
    {
        await Task.Delay(3000);
        Trace.WriteLine("第2個執行緒執行");
    });
    await Task.Run(async () =>
    {
        await Task.Delay(2000);
        Trace.WriteLine("第3個執行緒執行");
    });
}

執行:

對現有程式碼進行封裝:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Utils
{
    public class TaskQueue
    {
        /// <summary>
        /// 任務列表
        /// </summary>
        private List<Task> TaskList = null;
        /// <summary>
        /// 是否在執行任務中
        /// </summary>
        private bool isPerformTask = false;
        /// <summary>
        /// 執行完任務的回撥
        /// </summary>
        public Action CallBack = null;


        private static TaskQueue _instance = null;
        public static TaskQueue Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new TaskQueue();
                return _instance;
            }
        }

        /// <summary>
        /// 新增任務
        /// </summary>
        /// <param name="task"></param>
        public void AddTask(Task task)
        {
            if (isPerformTask)
            {
                Console.WriteLine("[TaskQueue]任務正在執行中,此時不能做賦值操作");
                return;
            }

            if (task != null)
            {
                TaskList.Add(task);
            }
        }

        /// <summary>
        /// 執行任務
        /// </summary>
        public void PerformTask()
        {
            if (isPerformTask)
            {
                Console.WriteLine("[TaskQueue]任務正在執行中,不可重複呼叫");
                return;
            }
            if (TaskList == null || TaskList.Count == 0)
            {
                Console.WriteLine("[TaskQueue]任務列表為空");
                return;
            }         

            Task.Run(() =>
            {
                isPerformTask = true;

                foreach (Task item in TaskList)
                {
                    item.Start();
                    item.Wait();
                }

                TaskList.Clear();
                isPerformTask = false;

                if (CallBack != null) CallBack();
            });
        }

        private TaskQueue()
        {
            TaskList = new List<Task>();
        }
    }
}

呼叫:

Task task1 = new Task(() =>
{
    Thread.Sleep(1000);
    Console.WriteLine("t1");
});
Task task2 = new Task(() =>
{
    Thread.Sleep(2000);
    Console.WriteLine("t2");
});
Task task3 = new Task(() =>
{
    Console.WriteLine("t3");
});
Action callback = () =>
{
    Console.WriteLine("所有任務執行完成");
};
TaskQueue.Instance.AddTask(task1);
TaskQueue.Instance.AddTask(task2);
TaskQueue.Instance.AddTask(task3);
TaskQueue.Instance.CallBack = callback;
TaskQueue.Instance.PerformTask();

執行:

 

如果你這個貼文對你有用,歡迎給我點贊 + 留言,謝謝

end