我有这个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可以访问两个线程,但应用程序冻结了 - 如何更改我的代码以解决此问题。
答案 0 :(得分:6)
运行此应用程序时,您不会遇到死锁。相反,您会收到一条错误,指出某些数据在DataTable
内已损坏,因为DataTable
本身有一些内部的并发检查。
我认为这个应用程序是真实应用程序的简单版本。您可以采取以下几个步骤来改进您的计划:
使用共享数据进行多线程处理很困难。您是否可以简化您的计划,以便您不需要任何共享数据?也许你可以并行运行两个操作,返回一个结果,然后合并这些结果。这样可以避免许多潜在的问题。
如果不可能,您可以执行以下操作:
您可以像这样创建一个锁定对象:
private static object _lock = new object();
现在,您可以使用以下命令对数据表进行调用:
lock (_lock)
{
dt.Rows.Add(i);
}
Task
对象是围绕Thread的智能包装器。建议使用任务。
Task t1 = Task.Run(() => P1(dt));
Task t2 = Task.Run(() => P2(dt));
Task.WaitAll(t1, t2);
由于最初的问题现已从死锁变为冻结,因此还需要考虑其他一些事项。
在控制台应用程序中,您将始终需要等待,直到完成两项任务。等待将冻结您的UI。当然,你可以使用像计时器这样的聪明的东西来检查任务是否已经完成,或者除了控制台应用程序之外的某些东西确实不适用于这样的情况。
但是,如果您的应用程序实际上是一个WinForms或WPF应用程序,您应该考虑使用异步代码和C#5中添加的新async / await关键字。它们与Tasks完美配合,它们允许您在另一个线程上运行代码UI保持响应。任务完成后,结果将合并回UI线程,用户将保留响应式应用程序。
有关async / await的更多信息,您可以从这里开始:Asynchronous Programming with Async and Await (C# and Visual Basic)
答案 1 :(得分:1)
除了Wouter de Kort的回答:
如果要在DataTable上使用锁定,请使用DataTable.Rows.SyncRoot对象,如
lock(dt.Rows.SyncRoot)
//Your code here
因为它是假定的锁定对象。