線程基本概念

来源:http://www.cnblogs.com/liyulongBlog/archive/2017/11/28/7911905.html
-Advertisement-
Play Games

原文:http://blog.sina.com.cn/s/blog_4e61c4290100ndyl.html 引言 隨著雙核、四核等多核處理器的推廣,多核處理器或超線程單核處理器的電腦已很常見,基於多核處理的編程技術也開始受到程式員們普遍關註。這其中一個重要的方面就是構建多線程應用程式(因為不使 ...


原文:http://blog.sina.com.cn/s/blog_4e61c4290100ndyl.html

 

引言

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

  本文針對的是構建基於單核電腦的多線程應用程式,目的在於介紹多線程相關的基本概念、內涵,以及如何通過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,

 

上一頁  [1] [2] [3] [4] [5] [6] 下一頁

歡迎進入.NET社區論壇,與200萬技術人員互動交流 >>進入

 

  這裡稍微對 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組件等三種手段構建多線程應用程式。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • EF Power Tools最高支持到vs2013,但是可以通過變更版本號的方式在vs2015上進行安裝。 參考文章:(英文)http://thedatafarm.com/data-access/installing-ef-power-tools-into-vs2015/ (中文)https://w ...
  • 演示產品下載地址:http://www.jinhusns.com ...
  • 返回總目錄 7 Introduce Null Object(引入Null對象) 概要 你需要再三檢查某對象是否為null。 將null值替換為null對象。 動機 系統在使用對象的相關功能時,總要檢查對象是否為null,如果不為null,我們才會調用它的相關方法,完成某種邏輯。這樣的檢查在一個系統中 ...
  • 網路層 客戶端和伺服器通過 TCP 連接來進行數據交互, 伺服器預設的埠號為 6379 。 客戶端和伺服器發送的命令或數據一律以 \r\n (CRLF)結尾。 請求 Redis 伺服器接受命令以及命令的參數。 伺服器會在接到命令之後,對命令進行處理,並將命令的回覆傳送回客戶端。 新版統一請求協議 ...
  • 在一個給客戶做的項目中,界面要求修改增加通用工具欄按鈕的事件處理,也就是在主界面中放置幾個固定的功能操作按鈕,打開不同的頁面的時候,實現對應頁面的功能處理,這種和我標準的界面處理方式有所不同,標準的列表界面,一般在界面中放置了一些常規的按鈕,如查詢/更新、新建、編輯、刪除、導入、導出等常規操作,現在... ...
  • Invoke 函數需要繼承 MonoBehaviour 類後才能使用。 Invoke(string str,float a):a 秒後執行名為 str 函數(只會調用一次)。 Invoke(string str,float a,float b):a 秒後執行名為 str 函數,並且以後每隔 b 秒都 ...
  • [MY NOTE] Translate Source:http://www.dotnettricks.com/learn/webapi/difference-between-wcf-and-web-api-and-wcf-rest-and-web-service Web Service 1.基於SO ...
  • Audio Source:聲音組件。需要與 Audio Listener 配合使用,Main Camera 會預設有 Audio Lisetener。 Audio Clip:聲音片段。指定需要播放的音頻文件。 Play On Awake:游戲運行起來後就開始播放。 Mute:靜音。 Loop:是否循 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...