多線程如何排隊執行

場景

有一個這樣場景,程序會有一個非常耗時的操作,但要求耗時的操作完成後,再順序的執行一個不耗時的操作,而且這個程序的調用,可能存在同時調用的情況。

具體的模型如下:

從Start開始觸發了5個線程,經過一個longTimeJob同時執行,我們不關心longJob的執行時間和先後順序,根據Start的先後順序來執行一個ShortJob。下面我們用代碼來模擬上面的過程。

舉例說明:有ABCD 4個線程,進入的順序也是ABCD,A耗時3s,B耗時7s,C耗時1s,D耗時3s. 所以如果當4個線程都同時開始執行時,完成的先後順序爲 CADB,但我們要求的順序是ABCD,也就是說C要等待AB執行完後,才能繼續後續的工作。

我們可以用請求bing搜索來模擬longTimeJob,根據傳入的序列來決定請求多少次,主要模擬方法如下:

    private static async Task Test()
    {
        var arry = new[]
        {
            10, 9, 8, 7, 6, 5, 4, 3, 2, 1
        };
        var listTask = new List<Task<int>>();
        foreach (var i in arry)
        {
            var i1 = i;
            var task = Task.Run(() => DoJob(i1));

            listTask.Add(task);
        }

        foreach (var task in listTask)
        {
            Console.WriteLine("輸出-->:" + await task);//
        }
    }     

    public static Task<int> DoJob(int o)
    {
        return Task.Run(() =>
        {
            DoLongTimeThing(o);
            return o;
        });
    }      

    public static void DoLongTimeThing(int i)
    {
        Console.WriteLine("執行-->:" + i);
        for (int j = 0; j < i; j++)
        {
            HttpGet("http://cn.bing.com/");
        }

        Console.WriteLine("執行完畢-->:" + i);
    }

    public static string HttpGet(string url)
    {
        try
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
            string content = reader.ReadToEnd();
            return content;
        }
        catch (Exception e)
        {
            return e.Message;
        }

    }

執行結果:

上面的代碼大概能解決我們的問題,有一個問題,對於客戶的調用我們無法形成一個List,而且list是線程安全的,所以針對上述的方法在實際的業務場景中無法使用。

新思路

我們無法實現一個有序的Task列表,如果換一個角度考慮,當一個任務形成的時間,同時生成一個對應的HashCode,對HashCode進行一個隊列的入隊操作,當執行完成longTimeJob後,判斷是不是隊列的第一個Task的HashCode,如果是則執行,如果不是則繼續等待,切換線程。 具體如下思路如下圖:

  public static Queue<string> Queue = new Queue<string>();
	static void Main(string[] args)
	{
	    var arry = new[]
	    {
	        10, 9, 8, 7, 6, 5, 4, 3, 2, 1
	    };

	    foreach (var i in arry)
	    {
	        Console.WriteLine("進入Job順序-->:" + i);
	        Test(i);
	    }
	    Console.ReadKey();
	}

	public static void Test(int i)
	{
	    var taskId = Guid.NewGuid().ToString();
	    Queue.Enqueue(taskId);
	    Task.Factory.StartNew(DoJob, new object[] { i, taskId });
	}
	public static void DoJob(object o)
	{
	    var oArry = (object[])o;
	    var n = (int)oArry[0];
	    var currId = oArry[1].ToString();

	    DoLongTimeThing(n);//

	    while (currId != Queue.Peek())
	    {
	        Thread.Sleep(1);//等線程切換
	    }

	    Console.WriteLine("DoShortJob輸出-->:" + n);//
	    //請求數據庫 
	    Queue.Dequeue();
	}
	public static void DoLongTimeThing(int i)
	{
	    Console.WriteLine("LongTimeJob執行-->:" + i);
	    for (int j = 0; j < i; j++)
	    {
	        HttpGet("http://cn.bing.com/");
	    }
	    Console.WriteLine("LongTimeJob執行完畢-->:" + i);
	}


	public static string HttpGet(string url)
	{
	    try
	    {
	        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
	        request.Method = "GET";

	        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
	        StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
	        string content = reader.ReadToEnd();
	        return content;
	    }
	    catch (Exception e)
	    {
	        return e.Message;
	    }

	}

運行結果如下:

雖然執行結果看起來很亂,但仔細比對可以發現最終的DoShortTime是按順序執行的。

(本文完)

作者:老付 如果覺得對您有幫助,可以下方的訂閱,或者選擇右側捐贈作者,如果有問題,請在捐贈後諮詢,謝謝合作 如有任何知識產權、版權問題或理論錯誤,還請指正。 自由轉載-非商用-非衍生-保持署名,請遵循:創意共享3.0許可證 交流請加羣113249828:點擊加羣 或發我郵件 [email protected]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章