Multithreading 在另一个线程中处理异常会创建另一个线程

Multithreading 在另一个线程中处理异常会创建另一个线程,multithreading,c#-4.0,thread-safety,Multithreading,C# 4.0,Thread Safety,我最近看到了以下代码: 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

我最近看到了以下代码:

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");
}
静态列表=新列表();
静态void Main(字符串[]参数)
{
var lines=File.ReadAllLines(args[0]);
foreach(行中的var行)
{
开始线程(行);
}
Console.WriteLine(“加入”);
foreach(列表中的线程)
{
thread.Join();
}
控制台。写入线(“结束”);
Console.ReadKey();
}
静态空心向上插入(对象o)
{
var args=o.ToString().Split(',');
尝试
{
使用(var con=new SqlConnection(Settings.Default.ConnString))
{
var cmd=new SqlCommand
{
连接=con,
CommandText=“插入帐户值(@p1、@p2、@p3、@p4、@p5)”
};
对于(变量索引=0;索引
我对线程不是很在行,我想知道当错误1205可能发生时,代码会是什么样子,然后用相同的方法再次运行另一个线程,并再次添加到te线程列表中。如果前一个异常cougt线程已完成并中止,则不应该对其进行检查吗?然后将其从列表中删除并添加新的

你的贡献真的很有帮助


谢谢。

你说得对,这段代码有几个问题

  • 列表的访问不以任何方式同步
  • 在错误1205之后,主线程连接和正在创建的新线程之间存在争用
我将废弃这段代码,并创建一个存储过程来为您执行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个计数初始化
condownEvent
,因为我希望将主线程视为工作线程。这将修复可能出现的任何微妙的争用条件,如果一个工作线程在主线程完成所有其他线程的旋转之前完成。每次启动一个新线程时,我调用
AddCount
,当该线程完成时,我调用
信号。当然,主线程通过调用
Wait
来等待一切


如果我想稍微改变代码的结构,我可能会通过
Task
使用任务,然后当出现错误1205时,我会创建一个子任务,并通过
TaskCreationOptions.AttachedToParent
将其附加到父任务。但是,这需要一些更重要的更改,我希望将更改保持在最低限度。

谢谢您的评论。但我想知道是否有人可以在客户端帮助当前的线程操作。比如说,你没有访问数据库的权限,无法访问你推荐的好地方的更改。你扔了一颗核弹,thanx伙计。You rock:)我第一次看到这段代码:对代码中许多错误的地方有很多评论