我最近看到了这段代码:
static List<Thread> list = new List<Thread>();
static void Main(string[] args)
{
var lines = File.ReadAllLines(args[0]);
foreach (var line in lines)
{
StartThread(line);
}
Console.WriteLine("JOIN");
foreach (Thread thread in list)
{
thread.Join();
}
Console.WriteLine("END");
Console.ReadKey();
}
static void Upsert(object o)
{
var args = o.ToString().Split(',');
try
{
using (var con = new SqlConnection(Settings.Default.ConnString))
{
var cmd = new SqlCommand
{
Connection = con,
CommandText = "INSERT INTO Accounts VALUES(@p1, @p2, @p3, @p4, @p5)"
};
for (var index = 0; index < args.Length; index++)
{
cmd.Parameters.AddWithValue(@"@p" + (index + 1), args[index]);
}
try
{
con.Open();
cmd.ExecuteNonQuery();
Console.WriteLine("INSERTED");
}
catch (SqlException e)
{
switch (e.Number)
{
case 2627:
cmd.CommandText =
"UPDATE Accounts SET Name=@p2, Email=@p3, Active=@p4, Birthday=@p5 WHERE ID = @p1";
cmd.ExecuteNonQuery();
Console.WriteLine("UPDATED");
break;
case 1205:
StartThread(o); // On exception isn't some Thread handling should happen?
break;
}
}
}
}
}
private static void StartThread(object o)
{
// Is it correct to add another thread to the list again? when exception happens? What about the thread that was running
var t = new Thread(Upsert)
{
Priority = ThreadPriority.Highest,
IsBackground = true
};
list.Add(t);
t.Start(o);
Console.WriteLine("NEW THREAD STARTED");
}
我在线程方面不是那么强大,当我发现错误1205时,我想知道该代码是否具体,并且再次使用相同的方法再次运行另一个线程再添加到te线程列表。如果完成并中止它,不应该检查以前的异常cought线程?然后从列表中删除它并添加新的?
您的贡献非常有用。
谢谢。
答案 0 :(得分:2)
你是对的,这个代码有几个问题。
list
的访问不会以任何方式同步。我会废弃此代码并创建一个为您执行upsert的存储过程。这样的事情在服务器端更容易处理。此外,无论如何,我并不特别喜欢这种情况下多线程的整体想法。
如果您想要最大速度,请通过C#代码读取文件并解析出来,以便将其分解为单个字段。然后使用SqlBulkCopy
将所有记录一次性抛出到临时登陆区域表中。最后,调用存储过程将临时着陆区中的记录传输到相应的生产表中。所有这些都可以在不使用任何工作线程的情况下完成,并且它可能也会明显更快。
<强>更新强>
如果您使用CountdownEvent
,则可以轻松修复代码。完全抛弃线程列表并实例化CountdownEvent
进行等待,而不是在所有线程上调用Join
。
static CountdownEvent complete = new CountdownEvent(1);
static void Main(string[] args)
{
var lines = File.ReadAllLines(args[0]);
foreach (var line in lines)
{
StartThread(line);
}
Console.WriteLine("JOIN");
complete.Signal();
complete.Wait();
Console.WriteLine("END");
Console.ReadKey();
}
然后像这样更改StartThread
。
private static void StartThread(object o)
{
complete.AddCount();
var t = new Thread(
() =>
{
try
{
Upsert(o);
}
finally
{
complete.Signal();
}
});
t.Priority = ThreadPriority.Highest;
t.IsBackground = true;
t.Start();
Console.WriteLine("NEW THREAD STARTED");
}
所以我正在做的是用{1}来初始化CoundownEvent
,因为我想把主线程看作是一个工作者。这将解决在主线程完成所有其他线程旋转之前,如果其中一个工作程序完成可能出现的任何微妙的竞争条件。每次我开始一个新线程时,我都会调用AddCount
,当该线程结束时,我会调用Signal
。当然,主线程通过调用Wait
来等待所有事情。
如果我想更改代码的结构,我可能会通过Task
使用任务,然后当出现错误1205时,我会创建一个子任务并通过{将其附加到父级{1}}。但是,这需要进行一些更重要的更改,我希望将更改保持在最低限度。