淺析C#中構建多線程應用程序

***************************************************

更多精彩,歡迎進入:http://shop115376623.taobao.com

***************************************************

引言

1.理解多線程

2.?線程異步與線程同步

3.創建多線程應用程序

3.1通過System.Threading命名空間的類構建

3.1.1異步調用線程

3.1.2并發問題

3.1.3線程同步

3.2通過委托構建多線程應用程序

3.2.1線程異步

3.2.2線程同步

3.3BackgroundWorker組件

4.總結


引言

?隨著雙核、四核等多核處理器的推廣,多核處理器或超線程單核處理器的計算機已很常見,基于多核處理的編程技術也開始受到程序員們普遍關注。這其中一個重要的方面就是構建多線程應用程序(因為不使用多線程的話,開發人員就不能充分發揮多核計算機的強大性能)。

?本文針對的是構建基于單核計算機的多線程應用程序,目的在于介紹多線程相關的基本概念、內涵,以及如何通過System.Threading命名空間的類、委托和BackgroundWorker組件等三種手段構建多線程應用程序。

?本文如果能為剛接觸多線程的朋友起到拋磚引玉的作用也就心滿意足了。當然,本人才疏學淺,文中難免會有不足或錯誤的地方,懇請各位朋友多多指點。


1.理解多線程

?我們通常理解的應用程序就是一個*.exe文件,當運行*.exe應用程序以后,系統會在內存中為該程序分配一定的空間,同時加載一些該程序所需的資源。其實這就可以稱為創建了一個進程,可以通過Windows任務管理器查看這個進程的相關信息,如映像名稱、用戶名、內存使用、PID(唯一的進程標示)等,如圖下所示。

而線程則只是進程中的一個基本執行單元。一個應用程序往往只有一個程序入口,如:

??? ????[STAThread]

? ? ? ??static?void?Main()???//應用程序主入口點

??????? {

????????????Application.EnableVisualStyles();

????????????Application.SetCompatibleTextRenderingDefault(false);

????????????Application.Run(new?MainForm());

? ? }
進程會包含一個進入此入口的線程,我們稱之為主線程。其中,特性?[STAThread]?指示應用程序的默認線程模型是單線程單元(相關信息可參考

http://msdn.microsoft.com/en-us/library/system.stathreadattribute(VS.71).aspx)。

只包含一個主線程的進程是線程安全的,相當于程序僅有一條工作線,只有完成了前面的任務才能執行排在后面的任務。

?然當在程序處理一個很耗時的任務,如輸出一個大的文件或遠程訪問數據庫等,此時的窗體界面程序對用戶而言基本像是沒反應一樣,菜單、按鈕等都用不了。因為窗體上控件的響應事件也是需要主線程來執行的,而主線程正忙著干其他的事,控件響應事件就只能排隊等著主線程忙完了再執行。

?為了克服單線程的這個缺陷,Win32 API可以讓主線程再創建其他的次線程,但不論是主線程還是次線程都是進程中獨立的執行單元,可以同時訪問共享的數據,這樣就有了多線程這個概念。

?相信到這,應該對多線程有個比較感性的認識了。但筆者在這要提醒一下,基于單核計算機的多線程其實只是操作系統施展的一個障眼法而已(但這不會干擾我們理解構建多線程應用程序的思路),他并不能縮短完成所有任務的時間,有時反而還會因為使用過多的線程而降低性能、延長時間。之所以這樣,是因為對于單CPU而言,在一個單位時間(也稱時間片)內,只能執行一個線程,即只能干一件事。當一個線程的時間片用完時,系統會將該線程掛起,下一個時間內再執行另一個線程,如此,CPU以時間片為間隔在多個線程之間交替執行運算(其實這里還與每個線程的優先級有關,級別高的會優先處理)。由于交替時間間隔很短,所以造成了各個線程都在“同時”工作的假象;而如果線程數目過多,由于系統掛起線程時要記錄線程當前的狀態數據等,這樣又勢必會降低程序的整體性能。但對于這些,多核計算機就能從本質上(真正的同時工作)提高程序的執行效率。


2.?線程異步與線程同步

?從線程執行任務的方式上可以分為線程同步和線程異步。而為了方便理解,后面描述中用“同步線程”指代與線程同步相關的線程,同樣,用“異步線程”表示與線程異步相關的線程。

?線程異步就是解決類似前面提到的執行耗時任務時界面控件不能使用的問題。如創建一個次線程去專門執行耗時的任務,而其他如界面控件響應這樣的任務交給另一個線程執行(往往由主線程執行)。這樣,兩個線程之間通過線程調度器短時間(時間片)內的切換,就模擬出多個任務“同時”被執行的效果。

?線程異步往往是通過創建多個線程執行多個任務,多個工作線同時開工,類似多輛在寬廣的公路上并行的汽車同時前進,互不干擾(讀者要明白,本質上并沒有“同時”,僅僅是操作系統玩的一個障眼法。但這個障眼法卻對提高我們的程序與用戶之間的交互、以及提高程序的友好性很有用,不是嗎)。

?在介紹線程同步之前,先介紹一個與此緊密相關的概念——并發問題

?前面提到,線程都是獨立的執行單元,可以訪問共享的數據。也就是說,在一個擁有多個次線程的程序中,每個線程都可以訪問同一個共享的數據。再稍加思考你會發現這樣可能會出問題:由于線程調度器會隨機的掛起某一個線程(前面介紹的線程間的切換),所以當線程a對共享數據D的訪問(修改、刪除等操作)完成之前被掛起,而此時線程b又恰好去訪問數據D,那么線程b訪問的則是一個不穩定的數據。這樣就會產生非常難以發現bug,由于是隨機發生的,產生的結果是不可預測的,這樣樣的bug也都很難重現和調試。這就是并發問題。

?為了解決多線程共同訪問一個共享資源(也稱互斥訪問)時產生的并發問題,線程同步就應運而生了。線程同步的機理,簡單的說,就是防止多個線程同時訪問某個共享的資源。做法很簡單,標記訪問某共享資源的那部分代碼,當程序運行到有標記的地方時,CLR(具體是什么可以先不管,只要知道它能控制就行)對各線程進行調整:如果已有線程在訪問一資源,CLR就會將其他訪問這一資源的線程掛起,直到前一線程結束對該資源的訪問。這樣就保證了同一時間只有一個線程訪問該資源。打個比方,就如某資源放在只有一獨木橋相連的孤島上,如果要使用該資源,大家就得排隊,一個一個來,前面的回來了,下一個再去,前面的沒回來,后面的就原地待命。

?這里只是把基本的概念及原理做了一個簡單的闡述,不至于看后面的程序時糊里糊涂的。具體如何編寫代碼,下面的段落將做詳細介紹。


3.創建多線程應用程序

? ?這里做一個簡單的說明:下面主要通過介紹通過System.Threading命名空間的類、委托和BackgroundWorker組件三種不同的手段構建多線程應用程序,具體會從線程異步和線程同步兩個方面來闡述。

3.1通過System.Threading命名空間的類構建

????在.NET平臺下,System.Threading命名空間提供了許多類型來構建多線程應用程序,可以說是專為多線程服務的。由于本文僅是想起到一個“拋磚引玉”的作用,所以對于這一塊不會探討過多、過深,主要使用System.Threading.Thread類。

?先從System.Threading.Thread類本身相關的一個小例子說起,代碼如下,解釋見注釋:
using?System;

using?System.Threading;?//引入System.Threading命名空間

namespace?MultiThread

{

????class?Class

??? {

????????static?void?Main(string[] args)

??????? {

????????????Console.WriteLine("**************?顯示當前線程的相關信息 *************");

????????????//聲明線程變量并賦值為當前線程

????????????Thread?primaryThread =?Thread.CurrentThread;

????????????//賦值線程的名稱

?????????? ?primaryThread.Name =?"主線程";

????????????//顯示線程的相關信息

????????????Console.WriteLine("線程的名字:{0}", primaryThread.Name);

????????????Console.WriteLine("線程是否啟動? {0}", primaryThread.IsAlive);

????????????Console.WriteLine("線程的優先級: {0}", primaryThread.Priority);

????????????Console.WriteLine("線程的狀態: {0}", primaryThread.ThreadState);

????????????Console.ReadLine();

??????? }

??? }

}

輸出結果如下:

**************?顯示當前線程的相關信息 *************

線程的名字:主線程

線程是否啟動? True

線程的優先級: Normal

線程的狀態: Running

?????對于上面的代碼不想做過多解釋,只說一下Thread.CurrentThread得到的是執行當前代碼的線程。

3.1.1異步調用線程

????這里先說一下前臺線程后臺線程。前臺線程能阻止應用程序的終止,既直到所有前臺線程終止后才會徹底關閉應用程序。而對后臺線程而言,當所有前臺線程終止時,后臺線程會被自動終止,不論后臺線程是否正在執行任務。默認情況下通過Thread.Start()方法創建的線程都自動為前臺線程,把線程的屬性IsBackground設為true時就將線程轉為后臺線程。

?????下面先看一個例子,該例子創建一個次線程執行打印數字的任務,而主線程則干其他的事,兩者同時進行,互不干擾。

using?System;

using?System.Threading;

using?System.Windows.Forms;

namespace?MultiThread

{

????class?Class

??? {

????????static?void?Main(string[] args)

??????? {

????????????Console.WriteLine("*************?兩個線程同時工作 *****************");

?????????? ?//主線程,因為獲得的是當前在執行Main()的線程

????????????Thread?primaryThread =?Thread.CurrentThread;

??????????? primaryThread.Name =?"主線程";

????????????Console.WriteLine("-> {0}?在執行主函數 Main()。",?Thread.CurrentThread.Name);

????????????//次線程,該線程指向PrintNumbers()方法

??????? ????Thread?SecondThread =?new?Thread(new?ThreadStart(PrintNumbers));

??????????? SecondThread.Name =?"次線程";

????????????//次線程開始執行指向的方法

??????????? SecondThread.Start();

?

????????????//同時主線程在執行主函數中的其他任務

????????????MessageBox.Show("正在執行主函數中的任務。。。。",?"主線程在工作...");

????????????Console.ReadLine();

??????? }

????????//打印數字的方法

????????static?void?PrintNumbers()

??????? {

????????????Console.WriteLine("-> {0}?在執行打印數字函數 PrintNumber()",?Thread.CurrentThread.Name);

????????????Console.WriteLine("打印數字: ");

?????????? ?for?(int?i = 0; i < 10; i++)

??????????? {

????????????????Console.Write("{0}, ", i);

????????????????//Sleep()方法使當前線程掛等待指定的時長在執行,這里主要是模仿打印任務

????????????????Thread.Sleep(2000);

??????????? }

????????????Console.WriteLine();

??????? }

??? }

}

程序運行后會看到一個窗口彈出,如圖所示,同時控制臺窗口也在不斷的顯示數字。

?

輸出結果為:

?

*************?兩個線程同時工作 *****************

->?主線程 在執行主函數 Main()。

->?次線程 在執行打印數字函數 PrintNumber()

打印數字:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9,

????這里稍微對 Thread SecondThread = new Thread(new ThreadStart(PrintNumbers)); 這一句做個解釋。其實 ThreadStart 是 System.Threading 命名空間下的一個委托,其聲明是 public delegate void ThreadStart(),指向不帶參數、返回值為空的方法。所以當使用 ThreadStart 時,對應的線程就只能調用不帶參數、返回值為空的方法。那非要指向含參數的方法呢?在System.Threading命名空間下還有一個ParameterizedThreadStart 委托,其聲明是 public delegate void ParameterizedThreadStart(object obj),可以指向含 object 類型參數的方法,這里不要忘了 object 可是所有類型的父類哦,有了它就可以通過創建各種自定義類型,如結構、類等傳遞很多參數了,這里就不再舉例說明了。

3.1.2并發問題

??????這里再通過一個例子讓大家切實體會一下前面說到的并發問題,然后再介紹線程同步。

using?System;

using?System.Threading;

namespace?MultiThread1

{

????class?Class

??? {

????????static?void?Main(string[] args)

??????? {

????????????Console.WriteLine("*********?并發問題演示 ***************");

????????????//創建一個打印對象實例

????????????Printer?printer =?new?Printer();

????????????//聲明一含5個線程對象的數組

????????????Thread[] threads =?new?Thread[10];

?

????????????for?(int?i = 0; i < 10; i++)

??????????? {

????????????????//將每一個線程都指向printer的PrintNumbers()方法

??????????????? threads[i] =?new?Thread(new?ThreadStart(printer.PrintNumbers));

????????????????//給每一個線程編號

??????????????? threads[i].Name = i.ToString() +"號線程";

?? ?????????}

?

????????????//開始執行所有線程

????????????foreach?(Thread?t?in?threads)

??????????????? t.Start();

????????????Console.ReadLine();

??????? }

??? }

????//打印類

????public?class?Printer

??? {

????????//打印數字的方法

????????public?void?PrintNumbers()

??????? {

????????????Console.WriteLine("-> {0}?正在執行打印任務,開始打印數字:",?Thread.CurrentThread.Name);

?

????????????for?(int?i = 0; i < 10; i++)

??????????? {

????????????????Random?r =?new?Random();

????????????????//為了增加沖突的幾率及,使各線程各自等待隨機的時長

????????????????Thread.Sleep(2000 * r.Next(5));????

????????????????//打印數字

????????????????Console.Write("{0} ", i);

??????????? }

????????????Console.WriteLine();

??????? }

??? }

}

上面的例子中,主線程產生的10個線程同時訪問同一個對象實例printer的方法PrintNumbers(),由于沒有鎖定共享資源(注意,這里是指控制臺),所以在PrintNumbers()輸出到控制臺之前,調用PrintNumbers()的線程很可能被掛起,但不知道什么時候(或是否有)掛起,導致得到不可預測的結果。如下是兩個不同的結果(當然,讀者的運行結果可能會是其他情形)。

?

情形一
情形二


3.1.3線程同步

????線程同步的訪問方式也稱為阻塞調用,即沒有執行完任務不返回,線程被掛起。可以使用C#中的lock關鍵字,在此關鍵字范圍類的代碼都將是線程安全的。lock關鍵字需定義一個標記,線程進入鎖定范圍是必須獲得這個標記。當鎖定的是一個實例級對象的私有方法時使用方法本身所在對象的引用就可以了,將上面例子中的打印類Printer稍做改動,添加lock關鍵字,代碼如下:

????//打印類

????public?class?Printer

??? {

????????public?void?PrintNumbers()

??????? {

????????????//使用lock關鍵字,鎖定d的代碼是線程安全的

????????????lock?(this)

??????????? {

????????????????Console.WriteLine("-> {0}?正在執行打印任務,開始打印數字:",?Thread.CurrentThread.Name);

?

????????????????for?(int?i = 0; i < 10; i++)

??????????????? {

????????????????????Random?r =?new?Random();

????????????????????//為了增加沖突的幾率及,使各線程各自等待隨機的時長

????????????????????Thread.Sleep(2000 * r.Next(5));

????????????????????//打印數字

????????????????????Console.Write("{0} ", i);

??????????????? }

????????????????Console.WriteLine();

??????????? }

??????? }

??? }

}

同步后執行結果如下:

?

也可以使用System.Threading命名空間下的Monitor類進行同步,兩者內涵是一樣的,但Monitor類更靈活,這里就不在做過多的探討,代碼如下:

????

?

????//打印類

????public?class?Printer

??? {

????????public?void?PrintNumbers()

??????? {

????????????Monitor.Enter(this);

????????????try

??????????? {

????????????????Console.WriteLine("-> {0}?正在執行打印任務,開始打印數字:",?Thread.CurrentThread.Name);

?

????????????????for?(int?i = 0; i < 10; i++)

??????????????? {

????????????????????Random?r =?new?Random();

????????????????????//為了增加沖突的幾率及,使各線程各自等待隨機的時長

????????????????????Thread.Sleep(2000 * r.Next(5));

????????????????????//打印數字

????????????????????Console.Write("{0} ", i);

??????????????? }

????????????????Console.WriteLine();

??????????? }

????????????finally

??????????? {

????????????????Monitor.Exit(this);

??????????? }

??????? }

}

輸出結果與上面的一樣。


3.2通過委托構建多線程應用程序


? ??在看下面的內容時要求對委托有一定的了解,如果不清楚的話推薦參考一下博客園張子陽的《C# 中的委托和事件》,里面對委托與事件進行由淺入深的較系統的講解:http://www.cnblogs.com/JimmyZhang/archive/2007/09/23/903360.html

?????這里先舉一個關于委托的簡單例子,具體解說見注釋:

using?System;

namespace?MultiThread

{

????//定義一個指向包含兩個int型參數、返回值為int型的函數的委托

????public?delegate?int?AddOp(int?x,?int?y);

????class?program

??? {

????????static?void?Main(string[] args)

??????? {

????????????//創建一個指向Add()方法的AddOp對象p

????????????AddOp?pAddOp =?new?AddOp(Add);

????????????//使用委托間接調用方法Add()

????????????Console.WriteLine("10 + 25 = {0}", pAddOp(10, 5));

????????????Console.ReadLine();

??????? }

????????//求和的函數

????????static?int?Add(int?x,?int?y)

??????? {

????????????int?sum = x + y;

????????????return?sum;

??????? }

??? }

}?

運行結果為:

10 + 25 = 15

3.2.1線程異步

????說明一下,這里不打算講解委托線程異步或同步的參數傳遞、獲取返回值等,只是做個一般性的開頭而已,如果后面有時間了再另外寫一篇關于多線程中參數傳遞、獲取返回值的文章。

?????注意觀察上面的例子會發現,直接使用委托實例?pAddOp(10, 5)?就調用了求和方法 Add()。很明顯,這個方法是由主線程執行的。然而,委托類型中還有另外兩個方法——BeginInvoke()和EndInvoke(),下面通過具體的例子來說明,將上面的例子做適當改動,如下:

using?System;

using?System.Threading;

using?System.Runtime.Remoting.Messaging;

namespace?MultiThread

{

????//聲明指向含兩個int型參數、返回值為int型的函數的委托

????public?delegate?int?AddOp(int?x,?int?y);

????class?Program

??? {

????????static?void?Main(string[] args)

??????? {

????????????Console.WriteLine("*******?委托異步線程 兩個線程“同時”工作 *********");

????????????//顯示主線程的唯一標示

????????????Console.WriteLine("調用Main()的主線程的線程ID是:{0}.",?Thread.CurrentThread.ManagedThreadId);

????????????//將委托實例指向Add()方法

????????????AddOp?pAddOp =?new?AddOp(Add);

????????????//開始委托次線程調用。委托BeginInvoke()方法返回的類型是IAsyncResult,

????????????//包含這委托指向方法結束返回的值,同時也是EndInvoke()方法參數

????????????IAsyncResult?iftAR = pAddOp.BeginInvoke(10, 10,?null,?null);

????????????Console.WriteLine(""nMain()方法中執行其他任務........"n");

????????????int?sum = pAddOp.EndInvoke(iftAR);

?????? ?????Console.WriteLine("10 + 10 = {0}.", sum);

????????????Console.ReadLine();

??????? }

????????//求和方法

????????static?int?Add(int?x,?int?y)

??????? {

????????????//指示調用該方法的線程ID,ManagedThreadId是線程的唯一標示

????????????Console.WriteLine("調用求和方法 Add()的線程ID是: {0}.",?Thread.CurrentThread.ManagedThreadId);

????????????//模擬一個過程,停留5秒

????????????Thread.Sleep(5000);

????????????int?sum = x + y;

????????????return?sum;

??????? }

??? }

}

運行結果如下:

*******?委托異步線程 兩個線程“同時”工作 *********

調用Main()的主線程的線程ID是:10.

?

Main()方法中執行其他任務........

?

調用求和方法 Add()的線程ID是: 7.

10 + 10 = 20.

3.2.2線程同步

?????委托中的線程同步主要涉及到上面使用的pAddOp.BeginInvoke(10, 10, null, null)方法中后面兩個為null的參數,具體的可以參考相關資料。這里代碼如下,解釋見代碼注釋:

using?System;

using?System.Threading;

using?System.Runtime.Remoting.Messaging;

namespace?MultiThread

{

????//聲明指向含兩個int型參數、返回值為int型的函數的委托

????public?delegate?int?AddOp(int?x,?int?y);

????class?Program

??? {

????????static?void?Main(string[] args)

??????? {

????????????Console.WriteLine("*******?線程同步,“阻塞”調用,兩個線程工作 *********");

????????????Console.WriteLine("Main() invokee on thread {0}.",?Thread.CurrentThread.ManagedThreadId);

//將委托實例指向Add()方法

????????????AddOp?pAddOp =?new?AddOp(Add);

????????????IAsyncResult?iftAR = pAddOp.BeginInvoke(10, 10,?null,?null);

????????????//判斷委托線程是否執行完任務,

????????????//沒有完成的話,主線程就做其他的事

????????????while?(!iftAR.IsCompleted)

??????????? {

????????????????Console.WriteLine("Main()方法工作中.......");

????????????????Thread.Sleep(1000);

??????????? }

????????????//獲得返回值

????????????int?answer = pAddOp.EndInvoke(iftAR);

????????????Console.WriteLine("10 + 10 = {0}.", answer);

????????????Console.ReadLine();

??????? }

????????//求和方法

????????static?int?Add(int?x,?int?y)

??????? {

????????????//指示調用該方法的線程ID,ManagedThreadId是線程的唯一標示

????????????Console.WriteLine("調用求和方法 Add()的線程ID是: {0}.",?Thread.CurrentThread.ManagedThreadId);

????????????//模擬一個過程,停留5秒

????????????Thread.Sleep(5000);

????????????int?sum = x + y;

????????????return?sum;

??????? }

??? }

}

運行結果如下:

*******?線程同步,“阻塞”調用,兩個線程工作 *********

Main() invokee on thread 10.

Main()方法工作中.......

調用求和方法 Add()的線程ID是: 7.

Main()方法工作中.......

Main()方法工作中.......

Main()方法工作中.......

Main()方法工作中.......

10 + 10 = 20.


3.3BackgroundWorker組件

??????BackgroundWorker組件位于工具箱中,用于方便的創建線程異步的程序。新建一個WindowsForms應用程序,界面如下:

?

????????private?void?button1_Click(object?sender,?EventArgs?e)

代碼如下,解釋參見注釋:

?

??????? {

????????????try

??????????? {

????????????????//獲得輸入的數字

????????????????int?numOne =?int.Parse(this.textBox1.Text);

????????????????int?numTwo =?int.Parse(this.textBox2.Text);

????????????????//實例化參數類

?????????????? ?AddParams?args =?new?AddParams(numOne, numTwo);

????????????????//調用RunWorkerAsync()生成后臺線程,同時傳入參數

????????????????this.backgroundWorker1.RunWorkerAsync(args);

??????????? }

????????????catch?(Exception?ex)

??????????? {

????????????????MessageBox.Show(ex.Message);

??????????? }

??????? }

?

????????//backgroundWorker新生成的線程開始工作

????????private?void?backgroundWorker1_DoWork(object?sender,?DoWorkEventArgs?e)

??????? {

????????????//獲取傳入的AddParams對象

????????????AddParams?args = (AddParams)e.Argument;

?????????? ?//停留5秒,模擬耗時任務

????????????Thread.Sleep(5000);

????????????//返回值

??????????? e.Result = args.a + args.b;

??????? }

?

????????//當backgroundWorker1的DoWork中的代碼執行完后會觸發該事件

????????//同時,其執行的結果會包含在RunWorkerCompletedEventArgs參數中

????????private?void?backgroundWorker1_RunWorkerCompleted(object?sender,?RunWorkerCompletedEventArgs?e)

??????? {

????????????//顯示運算結果

????????????MessageBox.Show("運行結果為:"?+ e.Result.ToString(),?"結果");

??????? }

??? }

?

????//參數類,這個類僅僅起到一個記錄并傳遞參數的作用

????class?AddParams

??? {

????????public?int?a, b;

????????public?AddParams(int?numb1,?int?numb2)

??????? {

??????????? a = numb1;

??????????? b = numb2;

??????? }

??? }

注意,在計算結果的同時,窗體可以隨意移動,也可以重新在文本框中輸入信息,這就說明主線程與backgroundWorker組件生成的線程是異步的。


4.總結

?????本文從線程、進程、應用程序的關系開始,介紹了一些關于多線程的基本概念,同時闡述了線程異步、線程同步及并發問題等。最后從應用角度出發,介紹了如何通過System.Threading命名空間的類、委托和BackgroundWorker組件等三種手段構建多線程應用程序。

?再次說一下,由于本人才疏學淺,文中難免會有不足或錯誤的地方,真誠期盼各位同道朋友批評指正。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/459077.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/459077.shtml
英文地址,請注明出處:http://en.pswp.cn/news/459077.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

58同城高性能移動Push推送平臺架構演進之路

2019獨角獸企業重金招聘Python工程師標準>>> 本文詳細講述58同城高性能移動Push推送平臺架構演進的三個階段&#xff0c;并介紹了什么是移動Push推送&#xff0c;為什么需要&#xff0c;原理和方案對比&#xff1b;移動Push推送第一階段&#xff08;單平臺&#xff…

Android Studio 插件

https://plugins.jetbrains.com/?androidstudio轉載于:https://www.cnblogs.com/xiaoyao095/p/4712552.html

resin指定java版本_resin的幾個常用配置

本文著重介紹resin的幾個常用配置注&#xff1a;1. 本文并非resin.conf的使用說明&#xff0c;只是從實用角度出發&#xff0c;按需分析常用的配置。更多的內容到官網學習吧&#xff1b;2. 基于resin3.x的探討。(友情提示&#xff1a;剛下載的resin中&#xff0c;resin.conf里的…

Android生命周期函數執行順序

轉載自&#xff1a;http://blog.csdn.net/intheair100/article/details/39061473 程序正常啟動&#xff1a;onCreate()->onStart()->onResume(); onCreate之后就會執行onStart,此時畫面已經顯示出來了&#xff0c;但還不能交互&#xff1b;然后執行onResume,此時可以與用…

hdoj-2028-Lowest common multiple plus

題目&#xff1a;Lowest common multiple plus 代碼&#xff1a; #include<stdio.h> int common(int a,int b)//計算最大公約數 {int ca%b,t0;if(b>a){tb;ba;at;}while(a%b!0){ca%b;ab;bc;}return b; } int q[105]; int main() {int n,i,j,t0;while(scanf("%d&qu…

C#系統命名空間

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** 一、基礎命名空間 System.Collections 包含了一些與集合相關的類型,比如列表,隊列,位數…

java不規則算法_分布式id生成算法 snowflake 詳解

背景在復雜分布式系統中&#xff0c;往往需要對大量的數據和消息進行唯一標識。如在支付流水號、訂單號等&#xff0c;隨者業務數據日漸增長&#xff0c;對數據分庫分表后需要有一個唯一ID來標識一條數據或消息&#xff0c;數據庫的自增ID顯然不能滿足需求&#xff0c;此時一個…

Android中下載、安裝和卸載(原)

應用場景&#xff1a;在檢查版本更新的時候經常需要從服務器端下載然后安裝到手機中 使用工具&#xff1a; XUtils&#xff0c;這個開源的框架真的是需要花大把時間去閱讀和理解的&#xff0c;十分有用的&#xff0c;on the way &#xff01; fighting&#xff01; 下載&#x…

Android加載圖片OOM錯誤解決方式

前幾天做項目的時候&#xff0c;甲方要求是PAD &#xff08;SAMSUNG P600 10.1寸 2560*1600&#xff09;的PAD上顯示高分辨率的大圖片。 SQLITE採用BOLD方式存儲圖片&#xff0c;這個存取過程就不說了哈&#xff0c;網上一大堆。 可是在加載/讀取/顯示圖片的時候會報OOM錯誤&am…

C# 對Ini文件操作(C# ini文件操作類)

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** /* C# 對Ini文件操作&#xff08;C# ini文件操作類&#xff09; [IniFiles.cs] 蝶曉…

python對文件進行讀寫操作

2019獨角獸企業重金招聘Python工程師標準>>> python進行文件讀寫的函數是open或file file_handler open(filename,,mode&#xff09; Table mode 模式描述r以讀方式打開文件&#xff0c;可讀取文件信息。w以寫方式打開文件&#xff0c;可向文件寫入信息。如文件存在…

android:contentDescription

android:contentDescription這個屬性相信大家并不陌生&#xff0c;在ImageButton的使用過程中如果不添加這個屬性會有警告信息。 那么android:contentDescription究竟是干什么的呢&#xff1f;今天查了下資料才知道這個屬性的真正作用。 該屬性為視力障礙的用戶提供方便&#x…

c#中bin,obj,properties文件夾的作用

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** Bin目錄用來存放編譯的結果&#xff0c;bin是二進制binrary的英文縮寫&#xff0c;因為…

getAttribute實例例java_Java ExifInterface.getAttribute方法代碼示例

import android.media.ExifInterface; //導入方法依賴的package包/類public static void copyExif(ExifInterface originalExif, int width, int height, String imageOutputPath) {String[] attributes new String[]{ExifInterface.TAG_APERTURE,ExifInterface.TAG_DATETIME,…

檢測SDWebImage有沒有緩存圖片 IOS 獲取網絡圖片大小

判斷圖片是否緩存NSURL *url [NSURL URLWithString:[model.content objectForKey:"image"]];//請求網絡地址數據的同步方法//因為這個方法在子線程(全局隊列)中執行,所以不需要考慮死線程的問題SDWebImageManager *manager [SDWebImageManager sharedManager];[man…

mac 下 使用 java運行 class 文件 總是提示 “錯誤: 找不到或無法加載主類”的解決方法...

發現問題 切換到mac平臺后&#xff0c;突然想寫點程序運行在mac下&#xff0c;想到mac自帶java&#xff0c;會方便好多。不過在這過程中遇到了麻煩&#xff1a; 總是提示 “錯誤: 找不到或無法加載主類” 工程結構 查了好久&#xff0c;終于找到原型所在&#xff0c;發現網上很…

[轉]VisualStudio如何組織解決方案的目錄結構

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** 解決方案與項目&#xff1a; 從VC6之后VC系列就使用解決方案&#xff08;Solution&…

java幾種刪除_幾種刪除Linux目錄的方法

在Linux中有很多方法可以刪除目錄&#xff0c;在圖形化界面可以利用文件管理器&#xff0c;或者通過終端刪除。本文將介紹在文本界面使用命令刪除目錄。使用rmdir刪除目錄Rmdir命令間成“remove directory”&#xff0c;用于刪除空目錄的命令。例如&#xff0c;刪除一個名為“M…

php公鑰模數,php – 如何從公共指數和RSA模數生成DER / PEM證書?

眾所周知,公鑰由公共指數和模數組成.我的問題是&#xff1a;如何從公共指數和RSA模數生成DER / PEM證書&#xff1f;非常感謝你提前.解決方法:使用公共指數和模數,你可能希望做的最好的事情是得到這樣的東西&#xff1a;-----BEGIN PUBLIC KEY-----MIGGAoGAfHlcdrcuOK6C02rbGR3…

C# DataTable的詳細用法

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** DataTable 是一個臨時保存數據的網格虛擬表(表示內存中數據的一個表。)。DataTable是A…