最近在寫一個http接口時用了DataTable這個強大的利器,接口用瀏覽器跑起來沒任何問題。當時也沒考慮並發問題,后來用一個壓力測試工具做大並發測試,1000+/s次速度測試。發現程序報錯了。程序報錯了第一反應還是去檢查代碼,是不是代碼出現問題。發現邏輯都是對的,然后用瀏覽器打開接口,發現一切OK;然后心想肯定是並發時多個線程操作導致的。
我們都知道在多線程的時候不同的線程訪問同一個資源的時候,用lock方法來達到線程同步,也就是同一個時刻同一個資源只能被一個線程操作。
我開始在操作DataTable的函數前面加上:
1 object oblock = new object(); 2 3 lock(oblock) 4 { 5 //我的dataTable對rows操作的代碼 6 }
信心滿滿的開始繼續用壓力測試工具來測試。結果還是報錯;因為是並發操作你幾乎不能用vs進行斷點調試;所以也不好找到具體的錯誤在什么地方。然后就try catch 打印錯誤日志找到具體異常原因和所在代碼行。提示的錯誤有時候都不盡相同;有“內部索引損壞”、"在位置0處沒有任何行" 等等。加了lock還報錯就感覺挺納悶的;當這段代碼被鎖定以后應該是不會被其他線程操作,也就不會導致行的索引出現問題。
后來找些資料發現,因為我們平時對DataTable的操作基本是Rows操作,所以我上面lock的代碼里面只鎖定了DataTable的Rows;但是因為我沒有操作Columns,所以其他線程還可以訪問DataTable的Columns集合。但是Rows是共享一個Columns的;罪魁禍首應該是沒有鎖住Columns導致的。
解決方法:
通過百度知道.Net里DataTable.Rows集合上提供.SyncRoot同步對象,正是為了在多線程環境下鎖住DataTable中的Rows集合同時也鎖住了Columns集合。
所以我們只需要在要操作DataTable代碼的前面加上:
1 //你要操作的 DataTable 2 DataTable dtb = new DataTable(); 3 lock(dtb.Rows.SyncRoot) 4 { 5 //你的DataTable處理代碼 6 }
注:並不是所有DataTable在多線程操作是都會報錯;視具體情況而定。但是為了線程安全在多線程下操作是最好加上Rows.SyncRoot鎖。