2012年8月28日 星期二

C# - Threading using Task


之前在【C# Threading Finial】裡面已經把目前我所知道 Threading 跟 UI 配合的方法 demo 了一次,
上次同事告訴我說在 .net 4.0 (VS2010) 裡面提供了一個新的方式 Task 來寫 Threading ,
所以我就把上次【C# Threading Finial】裡面的程式拿來改成用 Task 的方式,
測試了一下,的確是簡單了許多,
不過規矩還是一樣,跟 UI 的互動還是得交給主執行緒,
所以還是得用 Invoke 的方式來呼叫,
其實我覺得比較麻煩的還是跟 UI 的互動,其他的倒是還好,
接下來就來看看上次那個程式怎麼修改成 Task 的方式吧!
首先原來的 btnThreadingQuery_Click 就變成下面這樣啦!
改成用 Task.Factory.StartNew 的方式來呼叫 QueryMain()


private void btnThreadingQuery_Click(object sender, EventArgs e)
{
    //ThreadPool.QueueUserWorkItem(new WaitCallback(QueryMain));
    Task.Factory.StartNew(() =>
    {
        QueryMain();
    });
}


然後主程式主要的修改如下:


  • 之前 waitHandles 都不要。
  • 然後 object param 也不需要。
  • 原本用 ThreadPool.QueueUserWorkItem 改成用 Task.Factory.StartNew 。
  • 最後 WaitHandle.WaitAll 變成 Task.WaitAll 。

大致上就是這樣,程式碼少了很多變得很簡潔,
整段的程式碼如下:


//private void QueryMain(object param)
private void QueryMain()
{
    //waitHandles = new List<WaitHandle>();

    Cursor tmpCursor = this.Cursor;
    Label[] labels = { lblThreadingDuration, lblThreadingTable1Count,
                            lblThreadingTable1Duration, lblThreadingTable2Count,
                            lblThreadingTable2Duration };
    ResetLabel(labels);

    try
    {
        this.Invoke(new UpdateButtonEnableHandler(UpdateButtonEnable),
            new object[] { btnThreadingQuery, false });
        this.Invoke(new UpdateFormCursorHandler(UpdateFormCursor),
            new object[] { Cursors.WaitCursor });

        CalcTime calcTime = new CalcTime();

        //waitHandles.Add(new AutoResetEvent(false));
        //object param1 = new object[] { "table1", lblThreadingTable1Count,
            //lblThreadingTable1Duration, calcTime};
            //lblThreadingTable1Duration, calcTime, waitHandles[waitHandles.Count()-1]};

        //ThreadPool.QueueUserWorkItem(
            //new WaitCallback(QueryTableByThreading), param1);
        var task1 = Task.Factory.StartNew(() =>
        {
            QueryTableByThreading("table1", lblThreadingTable1Count, lblThreadingTable1Duration, calcTime);
        });

        //waitHandles.Add(new AutoResetEvent(false));
        //object param2 = new object[] { "table2 ", lblThreadingTable2Count,
            //lblThreadingTable2Duration, calcTime};
            //lblThreadingTable2Duration, calcTime, waitHandles[waitHandles.Count()-1]};

        //ThreadPool.QueueUserWorkItem(
            //new WaitCallback(QueryTableByThreading), param2);
        var task2 = Task.Factory.StartNew(() =>
        {
            QueryTableByThreading("table2", lblThreadingTable2Count, lblThreadingTable2Duration, calcTime);
        });

        //WaitHandle.WaitAll(waitHandles.ToArray());
        Task.WaitAll(task1, task2);
        //lblThreadingDuration.Text = calcTime.Duration();
        this.Invoke(new UpdateLabelTextHandler(UpdateLabelText),
            new object[] { lblThreadingDuration, calcTime.Duration() });
    }
    finally
    {
        this.Invoke(new UpdateFormCursorHandler(UpdateFormCursor),
            new object[] { tmpCursor });
        this.Invoke(new UpdateButtonEnableHandler(UpdateButtonEnable),
            new object[] { btnThreadingQuery, true });
    }
}


然後 QueryTableByThreading 就恢復成原來我們熟悉的寫法,
不用再去解 Object Param 了!程式如下:


//private void QueryTableByThreading(object param)
private void QueryTableByThreading(string table, Label count, Label duration, CalcTime ct)
{
    //object[] oa = (object[])param;

    //string table = oa[0].ToString();
    //Label count = (Label)oa[1];
    //Label duration = (Label)oa[2];
    //CalcTime ct = (CalcTime)oa[3];
    //AutoResetEvent are = (AutoResetEvent)oa[4];

    DataTable dt = DBUtility.GetTableFromDB(table);

    string time = ct.Duration();

    this.Invoke(new UpdateLabelTextHandler(UpdateLabelText),
        new object[] { count, dt.Rows.Count.ToString("N") });

    this.Invoke(new UpdateLabelTextHandler(UpdateLabelText),
        new object[] { duration, time });

    //are.Set();
}


不知道甚麼時候可以簡化跟 UI 的互動?

沒有留言:

張貼留言