C Sharp/多線程與同步
外觀
< C Sharp
C#提供了至少六種多線程並發的實現:
- 線程類:
var t = new Thread(BackgroundTask);t.Start("Thread");
- 異步委託:
Action<string> d = BackgroundTask; d.BeginInvoke("BeginInvoke", null, null);
- 線程池:
ThreadPool.QueueUserWorkItem(BackgroundTask, "ThreadPool");
- BackgroundWorker類:
BackgroundWorker _demoBGWorker = new BackgroundWorker(); _demoBGWorker.DoWork += myWorkHorse;_demoBGWorker.RunWorkerAsync();
- Task Parallel Library (TPL):
Task.Run(() => BackgroundTask("TPL"));
- async await
Thread
類
[編輯]System.Threading.Thread
類提供了基本功能。創建Thread
類實例,參數為ThreadStart
或ParameterizedThreadStart
指向委託函數。如下例:
using System;
using System.Threading;
public static class Program
{
private static void SecondThreadFunction()
{
while (true)
{
Console.WriteLine("Second thread says hello.");
Thread.Sleep(1000); // pause execution of the current thread for 1 second (1000 ms)
}
}
public static void Main()
{
Thread newThread = new Thread(new ThreadStart(SecondThreadFunction));
newThread.Start();
while (true)
{
Console.WriteLine("First thread says hello.");
Thread.Sleep(500); // pause execution of the current thread for half a second (500 ms)
}
}
}
void ParameterizedThreadStart(object obj)
委託允許給新線程傳遞一個參數:
using System;
using System.Threading;
public static class Program
{
private static void SecondThreadFunction(object param)
{
while (true)
{
Console.WriteLine("Second thread says " + param.ToString() + ".");
Thread.Sleep(500); // pause execution of the current thread for half a second (500 ms)
}
}
public static void Main()
{
Thread newThread = new Thread(new ParameterizedThreadStart(SecondThreadFunction));
newThread.Start(1234); // here you pass a parameter to the new thread
while (true)
{
Console.WriteLine("First thread says hello.");
Thread.Sleep(1000); // pause execution of the current thread for a second (1000 ms)
}
}
}
也可寫成:
using System;
using System.Threading;
Thread t2 = new Thread(() => { MyWorkHorseFunc(MyParam1); });
t2.Start();
共享數據
[編輯]下例2個線程訪問同一個變量,並不安全:
using System;
using System.Threading;
public static class Program
{
public static void Main()
{
int number = 1;
Thread newThread = new Thread(new ThreadStart(delegate
{
while (true)
{
number++;
Console.WriteLine("Second thread says " + number.ToString() + ".");
Thread.Sleep(1000);
}
}));
newThread.Start();
while (true)
{
number++;
Console.WriteLine("First thread says " + number.ToString() + ".");
Thread.Sleep(1000);
}
}
}
異步委託
[編輯]using System;
public static class Program
{
delegate int del(int[] data);
public static int SumOfNumbers(int[] data)
{
int sum = 0;
foreach (int number in data) {
sum += number;
}
return sum;
}
public static void Main()
{
int[] numbers = new int[] { 1, 2, 3, 4, 5 };
del func = SumOfNumbers;
IAsyncResult result = func.BeginInvoke(numbers, null, null);//异步调用,不阻塞
// I can do stuff here while numbers is being added
int sum = func.EndInvoke(result);//等待异步调用完成
//sum = 15
}
}
同步
[編輯]C#支持lock關鍵字,並在退出其作用域時解鎖:
using System;
using System.Threading;
public static class Program
{
public static void Main()
{
int number = 1;
object numberLock = new object();
Thread newThread = new Thread(new ThreadStart(delegate
{
while (true)
{
lock (numberLock)
{
number++;
Console.WriteLine("Second thread says " + number.ToString() + ".");
}
Thread.Sleep(1000);
}
}));
newThread.Start();
while (true)
{
lock (numberLock)
{
number++;
Console.WriteLine("First thread says " + number.ToString() + ".");
}
Thread.Sleep(1000);
}
}
}
Thread
類的Join
方法允許線程會合:
using System;
using System.Threading;
public static class Program
{
public static void Main()
{
Thread newThread = new Thread(new ThreadStart(delegate
{
Console.WriteLine("Second thread reporting.");
Thread.Sleep(5000);
Console.WriteLine("Second thread done sleeping.");
}));
newThread.Start();
Console.WriteLine("Just started second thread.");
newThread.Join(1000);
Console.WriteLine("First thread waited for 1 second.");
newThread.Join();
Console.WriteLine("First thread finished waiting for second thread. Press any key.");
Console.ReadKey();
}
}
任務並行庫
[編輯].NET 4引入了 Task Parallel Library (TPL)是當時最好的創建後台任務的方式。有強大的模型,支持鏈式任務、並行執行、等待一個或多個任務完成,傳遞cancellation token,甚至控制後台線程。
async和await
[編輯]C# 5和.NET 4.5引入了async和await。這允許你按照同步的方式寫源代碼但異步執行。可以await任何返回task的方法,對普通方法也可以用TPL包裝後await:
await Task.Run(() => xdoc.Load("http://feeds.feedburner.com/soundcode"));
一個特別好處是在UI線程中await一個方法時,會返回UI線程恢復執行:
await Task.Run(() => xdoc.Load("http://feeds.feedburner.com/soundcode"));
label1.Text = "Done"; // we’re back on the UI thread!
還可以使用try和catch包住異步線程:
private async void OnButtonAsyncAwaitClick(object sender, EventArgs e)
{
const string state = "Async Await";
this.Cursor = Cursors.WaitCursor;
try
{
label1.Text = String.Format("{0} Started", state);
await AwaitableBackgroundTask(state);
label1.Text = String.Format("About to load XML");
var xdoc = new XmlDocument();
await Task.Run(() => xdoc.Load("http://feeds.feedburner.com/soundcode"));
label1.Text = String.Format("{0} Done {1}", state, xdoc.FirstChild.Name);
}
catch (Exception ex)
{
label1.Text = ex.Message;
}
finally
{
this.Cursor = Cursors.Default;
}
}