如何确保一次只有1个线程可以访问DataTable
本文关键字:线程 1个 DataTable 访问 一次 何确保 确保 | 更新日期: 2023-09-27 18:02:46
我有这个Windows控制台应用程序:
using System;
using System.Text;
namespace ThreadLockSample
{
class Program
{
static void P1(System.Data.DataTable dt)
{
for (int i = 0; i < 100000; i++)
{
dt.Rows.Add(i);
}
}
static void P2(System.Data.DataTable dt)
{
for (int i = 100000; i < 200000; i++)
{
dt.Rows.Add(i);
}
}
static void Main(string[] args)
{
System.Data.DataTable dt = new System.Data.DataTable();
for (int i = 0; i < 200; i++)
dt.Columns.Add("Id " + i);
System.Threading.Thread t1 = new System.Threading.Thread(() => P1(dt));
System.Threading.Thread t2 = new System.Threading.Thread(() => P2(dt));
t1.Start();
t2.Start();
t1.Join();
t2.Join();
}
}
}
现在我正在传递一个数据表访问两个线程,但应用程序冻结-我怎么能改变我的代码,以消除这个问题。
运行此应用程序时,不会出现死锁。相反,您会得到一个错误,说明DataTable
中的一些数据损坏了,因为DataTable
本身有一些内部并发检查。
我想这个应用程序是一个简单的版本,你可以采取几个步骤来改进你的程序:
- 避免使用共享数据的多线程
如果这是不可能的,你可以这样做:
- <
- 加锁/gh>
创建一个锁对象,如下所示:
private static object _lock = new object();
现在你用:
包围对datatable的调用lock (_lock)
{
dt.Rows.Add(i);
}
- 不要直接使用线程。而是使用任务并行库:
Task
对象是围绕线程的智能包装器。建议使用Tasks
Task t1 = Task.Run(() => P1(dt));
Task t2 = Task.Run(() => P2(dt));
Task.WaitAll(t1, t2);
更新
由于原来的问题现在从死锁改为冻结,因此您需要考虑其他一些事情。
在控制台应用程序中,您总是需要等待两个任务完成。这种等待会冻结你的UI。当然,你可以使用定时器来检查任务是否完成,但控制台应用程序真的不适合这种情况。
然而,如果你的应用实际上是一个WinForms或WPF应用,你应该考虑使用c# 5中添加的async/await关键字的异步代码。它们与任务完美配合,让你在另一个线程上运行代码,同时UI保持响应。当Task完成时,结果被合并回你的UI线程,用户保持一个响应的应用程序。
有关async/await的更多信息,可以从这里开始:使用async和await进行异步编程(c#和Visual Basic)
除了Wouter de Kort的回答:
如果您想在数据表上使用锁,请使用DataTable. rows . syncroot对象,如
lock(dt.Rows.SyncRoot)
//Your code here
因为它是假定的锁对象