大話異步與並行(一)


很久沒安下心來寫博客了,幾年的開發過程中,對於異步與並行的了解也隨着清淅起來。首先很多人問我,異步與並行的區別,那么我們來了解下概念。

本博文寫的主旨是用最白話的語言來說明問題,不想照搬概念。

在古老的單核計算機中,一般是單核的,並行也只是在進程中交替的執行,表現出來的像並行執行一樣,只是時間比較短,在多核處理器的計算機中,進程不僅可以交替執行,而且可以重疊執行,所以說,並行,只有在多核處理器中才有真正意義。很多人可能會突然不理解,並行與並發,是什么區別,並行,就像兩種時刻相同的進程同一時刻運行,而並發不一定同一時刻運行,這就微妙的區別。

就拿訂單來說吧,在下單超大的情況下,A用戶下了一個訂單,還沒有來及結束,B用戶又下了一個訂單。那么,這時最有可能發行的情況就是並發事件。

那么我們今天重點說說異步,異步,是相對於同步來說的,我們知道,應用程序都是由一個主線程來運行的,這個主線程,是按照順序來處理我們寫的邏緝代碼的,這就是同步,引用異步的好處是,在不打撓主線程的前提下,繼續開放一個線程來指行其它的事情,就相當於,把部分工作交接給別人,當別人做好了后,然后,對我說,你交待的任務我已做好了。別人做好的工作交待給我的過程,相當於結果的返回中斷。

異步最終的目的就是給我們帶來更高效的時間效應,它是一種結果,而實現這個異步的可能是異步委托,線程池,線程等等,只不過是一種方法或途徑罷了,這就是線程與異步的最好詮釋。

下面來說說異步自然缺少不了多線程這個重頭戲。

實現多線程方式很多,下面一個一個的講起。

1.投票

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

namespace ConsoleApplication1
{
    class Program
    {
        public delegate decimal TakeDelegate(decimal data, int ms);  
  
        static void Main(string[] args)  
        {

            DateTime now = DateTime.Now;

            TakeDelegate dl = SaveBankAccount;
            IAsyncResult ar=  dl.BeginInvoke(1, 200, null, null);

            while(!ar.IsCompleted)
            {
                Console.WriteLine("main thread wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(50);
            }

            decimal result = dl.EndInvoke(ar);
            Console.WriteLine("CurrentMoney:{0}", result);
            Console.WriteLine("runtime:{0}", (DateTime.Now-now).TotalSeconds);
            Console.WriteLine("main thread IsBackground " + Thread.CurrentThread.IsBackground);
            Console.ReadKey();
        }
  
        static decimal SaveBankAccount(decimal money,int ms)
        {
            Console.WriteLine("SaveBankAccount thread started! current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("SaveBankAccount thread IsBackground " + Thread.CurrentThread.IsBackground);
            Thread.Sleep(ms);
            Console.WriteLine("SaveBankAccount thread completed!");
            return ++money;
        }

    }
}

一直都在想一個問題,為什么這種方式名字叫投票!百度 google都沒有結果,最后想着想着也就想清楚了,所謂投票,就是對結果的一種猜測,“是否結束投票”


2.異步回調

 

 

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

namespace ConsoleApplication1
{
    class Program
    {
        public delegate decimal TakeDelegate(decimal money,int ms);
        static DateTime now = DateTime.Now;

        static void Main(string[] args)  
        {
            TakeDelegate dl = SaveBankAccount;
            var ar = dl.BeginInvoke(1, 200, AsyncCallBack,dl);

            while(!ar.IsCompleted)
            {
                Console.WriteLine("main thread wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(50);
            }
            Console.ReadKey();
        }
  
        static decimal SaveBankAccount(decimal money,int ms)
        {
            Console.WriteLine("SaveBankAccount thread started! current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("SaveBankAccount thread IsBackground " + Thread.CurrentThread.IsBackground);
            Thread.Sleep(ms);
            Console.WriteLine("SaveBankAccount thread completed!");
            return ++money;
        }

        static void AsyncCallBack(IAsyncResult ar)
        {
            if (ar == null)
            {
                throw new ArgumentNullException("ar");
            }

            TakeDelegate dl = ar.AsyncState as TakeDelegate;

            decimal result = dl.EndInvoke(ar);
            Console.WriteLine("CurrentMoney:{0}", result);
            Console.WriteLine("runtime:{0}", (DateTime.Now - now).TotalSeconds);
            Console.WriteLine("main thread IsBackground " + Thread.CurrentThread.IsBackground);
         
        }
    }
}

這個方法是在投票的基礎,加入了回調函數而已。還有一種方法於投票差不多,就是等待句柄(AsyncWaitHandle),這個方法與投票沒有太大的差異。沒事的同學可以百度一下,這里就不多說了。

如果說到這里,那么,我想微軟也太失敗了,因為這樣玩異步太操心,那么多的代碼。

隨着時間的推移,微軟在.net3.0 C# 3.0的大包裹越來越健全 拉姆達(lambda)表達式與匿名方法 孕育而生。

好吧,我們對上面的代碼是時候要簡化的必要了。

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

namespace ConsoleApplication1
{
    class Program
    {


        static void Main(string[] args)
        {
            DateTime now = DateTime.Now;
            Func<decimal, int, decimal> f = SaveBankAccount;

            var ar = f.BeginInvoke(1, 200, (r) =>
            {
                if (r == null)
                {
                    throw new ArgumentNullException("r");
                }
                Console.WriteLine("CurrentMoney:{0}", f.EndInvoke(r));
                Console.WriteLine("runtime:{0}", (DateTime.Now - now).TotalSeconds);
                Console.WriteLine("main thread IsBackground " + Thread.CurrentThread.IsBackground);

            }, null);

            while (!ar.IsCompleted)
            {
                Console.WriteLine("main thread wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(50);
            }
            Console.ReadKey();
        }

        static decimal SaveBankAccount(decimal money, int ms)
        {
            Console.WriteLine("SaveBankAccount thread started! current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("SaveBankAccount thread IsBackground " + Thread.CurrentThread.IsBackground);
            Thread.Sleep(ms);
            Console.WriteLine("SaveBankAccount thread completed!");
            return ++money;
        }
    }
}

 

再次狠狠的優化

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

namespace ConsoleApplication1
{
    class Program
    {


        static void Main(string[] args)
        {
            DateTime now = DateTime.Now;
            Func<decimal, int, decimal> f = (money, ms) =>
            {
                Console.WriteLine("SaveBankAccount thread started! current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("SaveBankAccount thread IsBackground " + Thread.CurrentThread.IsBackground);
                Thread.Sleep(ms);
                Console.WriteLine("SaveBankAccount thread completed!");
                return ++money;
            };

            var ar = f.BeginInvoke(1, 200, (r) =>
            {
                if (r == null)
                {
                    throw new ArgumentNullException("r");
                }
                Console.WriteLine("CurrentMoney:{0}", f.EndInvoke(r));
                Console.WriteLine("runtime:{0}", (DateTime.Now - now).TotalSeconds);
                Console.WriteLine("main thread IsBackground " + Thread.CurrentThread.IsBackground);

            }, null);

            while (!ar.IsCompleted)
            {
                Console.WriteLine("main thread wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(50);
            }
            Console.ReadKey();
        }

    }
}

着重看下兩個划框的部分,lambda表達式采用匿名方法成功的簡化了傳統的方式,里面的參數獲取,將來的更為便捷,所以四個參數后,用了null。

public delegate TResult Func<in T, out TResult>(T arg) 委托 是微軟在.net 3.5 再次對delegate的封裝,第一個參數是輸入參數,第二個是返回參數,此時,我們不用太辛苦的到處聲明委托,這點微軟也給我們省了,不得不說,代碼的優美不是java能比的。

細心的朋友可能看到上面一段代碼 IsBackground 這個時候,表示線程是前台線程還是后台線程。

前台線程與后台線程的區別:

就像word文檔一樣,打開word 即開啟了word主線程即前台線程,諸如 語法檢查屬於后台線程,仔細想想,這樣設計,還是有道理的,當關閉了word前台主線程,后台線程語法檢查也沒有必要了。

一個進程里必須有一個前台線程,不一定有后台線程。當前台線程已結束的時候,后台線程也將結束。

看下面代碼

 

通常,應該將被動偵聽活動的線程設置為后台線程,而將負責發送數據的線程設置為前台線程,這樣,在所有的數據發送完畢之前該線程不會被終止。只有在確認線程被系統隨意終止沒有不利影響時,才應該使用后台線程。如果線程正在執行必須完成的敏感操作或事務操作,或者需要控制關閉線程的方式以便釋放重要資源,則使用前台線程。

例如 《C#高級編程》中有個例子--如果關閉Word程序,拼寫檢查器還在運行其進程就沒有意義了。在關閉應用程序時拼寫檢查器線程就可以關閉。

 小結:本節只是對於基礎知識線程與異步委托作了個簡單的復習,讓我聯想到,主線程也好,新開的線程也好,無非都是線程的部分,線程更多的是一種方法,而異步是一個需要線程支撐的結果,所以可以在任何線程上開啟異步的操作,因為主線程都可以開啟異步嘛。

 

 未完待續...

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM