對於所有需要等待 的操作,例 如 ,因 為文件 、 數據庫或網絡訪 問都需要一定 的時間,此 時就可以啟 動一個新線程,同時完成其他任務,即使是處理密集型的任務,線程也是有幫助的。
Parallel.For()方法類似於C#的For循環,多次執行一個任務,它可以並行運行迭代。迭代的順序沒有定義。
ParallelLoopResult result = Parallel.For(0, 10, i =>
{
Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(10);
});
Console.WriteLine(result.IsCompleted);
在For()方法中,前兩個參數定義了循環的開頭和結束。從輸出可以看出,順序是不能保證的。也可以提前中斷Parallel.For()方法。
ParallelLoopResult result2 = Parallel.For(10, 40, (int i,ParallelLoopState pls) =>
{
Console.WriteLine("i: {0},task:{1}", i, Task.CurrentId);
Thread.Sleep(10);
if (i > 15)
pls.Break();
});
Console.WriteLine(result2.IsCompleted);
Console.WriteLine( "lowest break iteration:{0}",result2.LowestBreakIteration);
paraller.ForEach()方法遍歷實現了IEnumerable的集合,其方式類似於Foreach語句,但以異步方式遍歷,這裡也沒有確定的遍歷順序。
string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" };
// Parallel.ForEach(data, s => { Console.WriteLine(s); });
Parallel.ForEach(data, (s, pls) => { if (s == "one") { Console.WriteLine("break......"); pls.Break(); } Console.WriteLine(s); Thread.Sleep(100); });
Parallel.Invoke(Foo ,Bar);
static void Foo() { Console.WriteLine("foo"); }
static void Bar() { Console.WriteLine("bar"); }
.NET 4 包含新的名稱空間System.Threading.Task,它它 包含的類抽象出了線程功能,在後台使用ThreadPool。 任務表示應完成的某個單元的工作。 這個單元的工作可以在單獨的線程中運行,也可以以同步方式啟動一個任務,這需要等待主調線程。
要啟動任務,可 以使用 TaskFactory類 或 Task類 的構造函數和 start()方 法。 Task類 的構造函數在創建任務上提供的靈活性較大.
//using TaskFactory
Task t1 = new TaskFactory().StartNew(TaskMethod);
//using the task factory via task
Task t2 = Task.Factory.StartNew(TaskMethod);
//using task constructor
Task t3 = new Task(TaskMethod);
t3.Start();
使用 Task類 的構造函數和 TaskFactory類 的 stamw()方法時,都可以傳遞TaskCreationOptions枚舉中的值。 設置LongRunning選項,可 以通知任務調度器,該 任務需要較長時間執行,這樣調度器更可能使用 新線。 如果該任務應關聯到父任務上,而父任務取消了,則 該任務也應取消,此 時應設置 AuachToParent選 項。PerferFairness 值表示,調度器應提取出已在等待的第一個任務。 如果任務使用 子任務創建了其他工作,子
任務就優先於其他任務。 它們不會排在線程池隊列中的最後。 如果這些任務應 以公平的方式與所有其他任務一起處理,就設置該選項為PreferFairness
Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness);
通過任務,可 以指定在任務完成後,應 開始運行另一個特定任務.
static void DoOnFirst()
{
Console.WriteLine("doing some task {0}",Task.CurrentId);
Thread.Sleep(3000);
}
static void DoSecond(Task t)
{
Console.WriteLine("task {0} finished",t.Id);
Console.WriteLine("this task id {0}",Task.CurrentId);
Console.WriteLine("do some cleanup");
Thread.Sleep(3000);
}
Task t1 = new Task(DoOnFirst);
Task t2 = t1.ContinueWith(DoSecond);
Task t3 = t2.ContinueWith(DoSecond);
Task t4 = t3.ContinueWith(DoSecond);
Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness);
t1.Start();
無論前一個任務是如何結束的,前 面 的連續任務總是在前一個任務結束時啟 動 。 使用TaskContinuationOptions 枚舉中的值,可 以指定,連續任務只有在起始任務成功(或失敗)結束時啟動。
static void ParentAndChild()
{
var parent = new Task(ParentTask);
parent.Start();
Thread.Sleep(2000);
Console.WriteLine(parent.Status);
Thread.Sleep(4000);
Console.WriteLine(parent.Status);
Console.WriteLine();
}
private static void ParentTask()
{
Console.WriteLine("task id {0}",Task.CurrentId);
var child = new Task(ChildTask);
child.Start();
Thread.Sleep(1000);
Console.WriteLine("parent started child");
}
private static void ChildTask()
{
Console.WriteLine("child");
Thread.Sleep(5000);
Console.WriteLine("child finished");
}
如果父任務在子任務之前結束 ,父 任務的狀態就顯示為WaitingForChildrenToComplete.只要子任務也結束 時,父任務的狀態就變成RanToCompletion。 ·
4 取消架構
var cts = new CancellationTokenSource();
cts.Token.Register(() => Console.WriteLine("token canceled"));
new Task(() => { Thread.Sleep(500); cts.Cancel(false); }).Start();
try
{
ParallelLoopResult result = Parallel.For(0, 100, new ParallelOptions() { CancellationToken = cts.Token, }, x =>
{
Console.WriteLine("loop {0} started", x);
int sun = 0;
for (int i = 0; i < 100; i++)
{
Thread.Sleep(2);
sun += i;
}
Console.WriteLine("loop {0} finished",x);
});
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
同樣的取消模式也可用於任務。
如果有不同的小任務要完成,就可以事先創建許多線程 ,· 在應完成這些任務時發出請求。 這個線程數最好在需要更多的線程時增加,在 需要釋放資源時減少。不需要自己創建這樣一個列表。 該列表由 ThreadPool類 托管。 這個類會在需要時增減池中線程的線程數,直 到最大的線程數。 池中的最大線程數是可配置的。如果有更多的作業要處理,線 程池中線程的個數也到了極限,最 新的作業就要排隊,且 必須等待線程完成其任務。

static void Main(string[] args)
{
int nWorkerThreads;
int nCompletionPortThreads;
ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPortThreads);
Console.WriteLine("nWorkerThreads:{0},nCompletionPortThreads:{1}", nWorkerThreads, nCompletionPortThreads);
for (int i = 0; i < 5; i++)
{
ThreadPool.QueueUserWorkItem(JobForAThread);
}
Thread.Sleep(3000);
Console.ReadKey();
}
static void JobForAThread(object obj)
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("loop:{0},running inside pooled thread{1}",i,Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(30);
}
}
View Code
線程池使用起來很簡單,但 它有一些限制 :
使用Thread類可以創建和控制線程,
new Thread(() => { Console.WriteLine("Running in thread"); }).Start();
Console.WriteLine("this is the main thread");
給線程傳遞一些數據可以采用2中方式,一種是使用帶ParameterizdThreadStart委托參數的Thread構造函數,另一種方式是常見一個自定義的類,把線程的方法定義為實例方法。
只要有一個前台相稱在運行,程序的進程就在運行,如果前台多個線程在運行,而Main()方法結束了,應用程序的進程直到所有前台完成其任務前都處於激活狀態。默認情況下,用Thread創建的線程為前台線程,線程池中的線程為總是為後台線程。Thread類可以設置IsBackground屬性設置是否為前台線程。

static void Main(string[] args)
{
var t1 = new Thread(ThreadMain) { Name = "NewThread", IsBackground = false };
t1.Start();
Console.WriteLine("Main thread ending now");
Console.ReadKey();
}
static void ThreadMain()
{
Console.WriteLine("Thread {0} statrted",Thread.CurrentThread.Name);
Thread.Sleep(5000);
Console.WriteLine("Thread {0} completed",Thread.CurrentThread.Name);
}
View Code
線 程曲操作系統調度。 給線程指定優先級,就 可 以影響調度順序。在Thread類中,可以設置Priority屬性設置線程的優先級,Priority屬性需要ThreadPriority枚舉定義的一個值,定義級別有Highest,AboveNormal,Normal,BelowNormal和Lowest。
調用 Thread對 象的Start()方 法,可 以創建線程。 但是,在 調用Strat()方法後,新線程仍不是處於 Running狀態,而 是處於 Unstarted狀 態。 只要操作系統的線程調度器選擇了要運行的線程,線程就會改為Running狀態 。 讀取Thread.ThreadState屬 性,就可以獲得線程的當前狀態。使用 Thread.Sleep() 方法 ,會使線程處於WaitSleepJoin狀態,在 經歷Sleep()方法定義的時間段後 ,線程就會等待再次被喚醒。要停止另一個線程,可 以調用Thread.Abort()方 法。 調用這個方法時,會 在接到終止命令的線程中拋出一個ThreadAbortException類 型的異常。 用一個處理程序捕獲這個異常,線程可 以在結束前完成一些清理工作。如 果需要等待線程的結束,就 可 以調用Thread.Join()方 法 。此方 法會停止當前線程 ,並把它設置為WaitSleepJoin狀 態 ,直 到加入 的線程完成為止 。
如果兩個或多個線程訪問相同的對象,或 者訪問不同步的共享狀態,就會出現爭用條件。
過多的鎖定也會有麻煩。 在死鎖中,至少有兩個線程被掛起,並等待對方解除鎖定。 由於兩個線程都在等待對方,就 出現了死鎖,線程將無限等待下去。
C#為多個線程的同步提供了 自己的關鍵字:lock語 句 。 lock語 句是設置鎖定和解除鎖定的一種簡單方式。

static void Main()
{
int numTask = 20;
var state = new ShareState();
var tasks = new Task[numTask];
for (int i = 0; i < numTask; i++)
{
tasks[i] = new Task(new Job(state).DoWork);
tasks[i].Start();
}
for (int i = 0; i < numTask; i++)
{
tasks[i].Wait();
}
Console.WriteLine("Sun :{0}",state.State);
Console.ReadKey();
}
}
public class Job
{
ShareState shareState;
public Job(ShareState shareState)
{
this.shareState = shareState;
}
public void DoWork()
{
for (int i = 0; i < 5000; i++)
{
shareState.State += 1;
}
}
}
public class ShareState
{
public int State { get; set; }
}
View Code
上面的代碼,因為執行了5000次循環,有20個任務,所以輸出的值應為100000,但是,事實並非如此。使用Lock修改DoWork方法

public void DoWork()
{
for (int i = 0; i < 5000; i++)
{
lock (shareState)
shareState.State += 1;
}
}
View Code
這樣結果總是正確的。但是在一個地方使用Lock語句並不意味著,訪問對象的其他線程都在等待,必須對每個訪問共享狀態的線程顯示的使用同步功能。繼續需改

static void Main()
{
int numTask = 20;
var state = new ShareState();
var tasks = new Task[numTask];
for (int i = 0; i < numTask; i++)
{
tasks[i] = new Task(new Job(state).DoWork);
tasks[i].Start();
}
for (int i = 0; i < numTask; i++)
{
tasks[i].Wait();
}
Console.WriteLine("Sun :{0}", state.State);
Console.ReadKey();
}
}
public class Job
{
ShareState shareState;
public Job(ShareState shareState)
{
this.shareState = shareState;
}
public void DoWork()
{
for (int i = 0; i < 5000; i++)
{
shareState.IncrementState();
}
}
}
public class ShareState
{
private int state = 0;
private object obj = new object();
public int State
{
get
{
return state;
}
}
public int IncrementState()
{
lock(obj)
return ++state;
}
}
View Code
Ihterlockcd類用 於使變量的簡單語旬原子化。 i++不是線程安全的,它 的操作包括從內存中獲取一個值,給該值遞增 1,再 將它存儲回內存。 這些操作都可能會被線程調度器打斷。 Ihterlocked類提供了以線程安全的方式遞增、 遞減、'交換和讀取值的方法。 與其他同步技術相 比,使用 Ihterlocked類 會快得多。 但是,它 只能用於簡單的同步問題。
C#的lock語 句 ,由編譯器解析為使用monitor類,與C#的 lock語 句相 比,Monitor 類的主要優點是:可 以添加一個等待被鎖定的超時值 。 這樣就不會無限期地等待被鎖定.

object obj = new object();
bool lockTaken = false;
Monitor.TryEnter(obj, 500, ref lockTaken);
if (lockTaken)
{
try
{
//已經鎖定,想干嘛就干嘛吧
}
finally
{
Monitor.Exit(obj);
}
}
else
{
//沒有鎖定,小心喽
}
View Code
Mutex【Mutual exclusion ,互 斥)是.Net Freamwork中 提供跨多個進程同步訪問的一個類 由於系統能識別有名稱的互斥,因 此可 以使用 它禁止應用程序啟動兩次

static class Program
{
/// <summary>
/// 應用程序的主入口點。
/// </summary>
[STAThread]
static void Main()
{
bool createNew;
Mutex m = new Mutex(false, "test", out createNew);
if (!createNew)
{
MessageBox.Show("程序已啟動");
Application.Exit();
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
View Code