Microsoft .Net Framework 提供了一個標准的取消操作的模式。這個模式是協作式的,意味著你想取消的操作必須顯示地支持取消。
CLR為我們提供了兩個類:
System.Threading.CancellationTokenSource
System.Threading.CancellationToken
CancellationToken實例是一個輕量級的值類型,因為它包含單個私有字段:CancellationTokenSource的一個引用。在一個計算限制操作的循環中,可以定時調用CancellationToken的IsCancellationRequested屬性,了解循環是否應該提前終止,進而終止計算機限制的操作。
我附上我常用代碼:
CancellationTokenSource cancel=new CancellationTokenSource(); Task.Factory.StartNew(() => { while (!cancel.IsCancellationRequested) { //do something } }, cancel.Token);// cancel.Token->CancellationToken //取消操作 cancel.Cancel(); cancel.Dispose(); cancel=null;
如果用到線程池,也可以使用這個類
public void main() { var cancel=new CancellationTokenSource(); ThreadPool.QueueUserWorkItem(_ => { while (!cancel.IsCancellationRequested) { //do something } }); Thread.Sleep(10 * 1000);//為了讓循環多做些時間 cancel.Cancel(); cancel.Dispose(); }
這個類還有一個很好的地方,可以在自定義回調函數,在調用Cancel方法的時候使用。這個我們需要用到CancellationToken裡面的Register方法(也就是new CancellationTokenSource().Token.Register(()=>{})),並且可以給一個Token多次注冊,按照倒序執行。
var cancel = new CancellationTokenSource(); cancel.Token.Register(() => { MessageBox.Show("Register3"); }); cancel.Token.Register(() => { MessageBox.Show("Register"); }); cancel.Token.Register(() => { MessageBox.Show("Register1"); }); cancel.Token.Register(() => { MessageBox.Show("Register2"); });
彈出順序:Register2,Register1,Register,Register3
如果想注銷注冊的回調函數,需要用到CancellationTokenRegistration(這個在調用Register的時候就會返回),附代碼:
CancellationTokenRegistration registration = cancel.Token.Register(() => { MessageBox.Show("Register2"); }); registration.Dispose();//這裡可以取消
取消以後再跑,就只會彈出另外三個,還是倒序。
最後,可通過鏈接另一組CancellationTokenSource來新建一個CancellationTokenSource對象,任何鏈接的CancellationTokenSource被取消,這個新的CancellationTokenSource對象就會自動被取消,附代碼:
public static void Go() { var cts1 = new CancellationTokenSource(); cts1.Token.Register(() => Console.WriteLine("cts1 canceled")); var cts2 = new CancellationTokenSource(); cts2.Token.Register(() => Console.WriteLine("cts2 canceled")); var linkedcts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token); linkedcts.Token.Register(() => Console.WriteLine("linkedcts canceled")); cts2.Cancel(); Console.WriteLine("cts1:{0} cts2:{1} linkedcts:{2}", cts1.IsCancellationRequested, cts2.IsCancellationRequested, linkedcts.IsCancellationRequested); }
由於cts2對象被取消了,所以linkedcts自動被取消,這裡CancellationTokenSource.CreateLinkedTokenSource 有一個重載是params CancellationToken[], 理論上說,無論加多少個CancellationToken對象都是可以的。
形式參數和實際參數
函數的參數分為形參和實參兩種。在本小節中,進一步介紹形參、實參的特點和兩者的關系。形參出現在函數定義中,在整個函數體內都可以使用,離開該函數則不能使用。實參出現在主調函數中,進入被調函數後,實參變量也不能使用。形參和實參的功能是作數據傳送。發生函數調用時,主調函數把實參的值傳送給被調函數的形參從而實現主調函數向被調函數的數據傳送。
函數的形參和實參具有以下特點:
1. 形參變量只有在被調用時才分配內存單元,在調用結束時,即刻釋放所分配的內存單元。因此,形參只有在函數內部有效。函數調用結束返回主調函數後則不能再使用該形參變量。
2. 實參可以是常量、變量、表達式、函數等,無論實參是何種類型的量,在進行函數調用時,它們都必須具有確定的值,以便把這些值傳送給形參。因此應預先用賦值,輸入等辦法使實參獲得確定值。
3. 實參和形參在數量上,類型上,順序上應嚴格一致,否則會發生類型不匹配”的錯誤。
4. 函數調用中發生的數據傳送是單向的。即只能把實參的值傳送給形參,而不能把形參的值反向地傳送給實參。 因此在函數調用過程中,形參的值發生改變,而實參中的值不會變化。
【例】可以說明這個問題。
main()
{
int n;
printf("input number\n");
scanf("%d",&n);
s(n);
printf("n=%d\n",n);
}
int s(int n)
{
int i;
for(i=n-1;i>=1;i--)
n=n+i;
printf("n=%d\n",n);
}
本程序中定義了一個函數s,該函數的功能是求∑ni的值。在主函數中輸入n值,並作為實參,在調用時傳送給s 函數的形參量n( 注意,本例的形參變量和實參變量的標識符都為n,但這是兩個不同的量,各自的作用域不同)。在主函數中用printf 語句輸出一次n值,這個n值是實參n的值。在函數s中也用printf 語句輸出了一次n值,這個n值是形參最後取得的n值0。從運行情況看,輸入n值為100。即實參n的值為100。把此值傳給函數s時,形參n的初值也為100,在執行函數過程中,形參n的值變為5050。返回主函數之後,輸出實參n的值仍為100。可見實參的值不隨形參的變化而變化。
fflush(stdin)
清空標准輸入緩存
#include "stdio.h"
main()
{
char a,b;
scanf("%c",&a);
//fflush(stdin);
scanf("%c",&b);
printf("\n%c %c",a,b);
}
可以嘗試,沒有fflush(stdin)時,輸入一串字符"abcd",則a='a',b='b'
如果有fflush(stdin),則在輸入"abcd"後程序會繼續等待輸入,再輸入"efdfsd",結果a='a',b='e'