【问题标题】:Threading C# lot slower then expected线程 C# 比预期慢很多
【发布时间】:2018-03-13 14:12:01
【问题描述】:

我们在使用线程来加速进程导入数据时遇到问题,但我们无法解决,因此我们将其分解为多个部分。

一开始,我们有一个包含 100 万行的人工主键的数据源,我们假设获取数据,将其转换为 XML 并将其插入到另一个数据源中。想法是拥有更多线程,每个线程平均拆分行并完成工作,并行工作应该加快整个过程,对吗?但事实并非如此,因此我们只专注于每个线程拆分行,将其转换为 XML 并将其附加到内存中的某个数据表中以计算时间。

这是我们的工作:

private void btn_Start_Click(object sender, EventArgs e)
{
    Thread MyThread = new Thread(Action_Start);
    MyThread.Start();
}

void Action_Start()
{
    string _Threads = text_Threads.Text;    // obtaining amount of threads 
    string _Bucket = text_Bucket.Text;      // obtaining amount of rows to 
                                            //          process per thread

    List<Task> MyTasks = new List<Task>();

    for (int Index = 1; Index <= Convert.ToInt32(_Threads); Index++)
    {
        int MyIndex = Index;
        MyTasks.Add(
            Task.Factory.StartNew(
                () => DoWork(MyIndex, Convert.ToInt32(_Bucket))));
    }

    Task.WaitAll(MyTasks.ToArray());
}

async void DoWork(int p_Index, int p_Bucket)
{
    DataTable MyTable = new DataTable();

    for (int Index = 1; Index <= 20; Index++)
    {
        DataColumn MyColumn = new DataColumn("FIELD_" + 
                                  Index.ToString("0000"), typeof(String));
        MyTable.Columns.Add(MyColumn);
    }

    for (int Index = 1; Index <= p_Bucket; Index++)
    {
        DataRow MyRow = MyTable.NewRow();

        for (int Index2 = 1; Index2 <= 20; Index2++)
        {
            string MyField = "FIELD_" + Index2.ToString("0000");
            MyRow[MyField] = new String('0', 128);
        }
        MyTable.Rows.Add(MyRow);
    }

    Stopwatch MyTimer = new Stopwatch();
    long Brojac = 1;
    DataTableReader MyReader = MyTable.CreateDataReader();

    MyTimer.Start();

    while (await MyReader.ReadAsync())
    {
        string Result = "<Root>";
        for (int Index = 1; Index <= 20; Index++)
        {
            string MyField = "FIELD_" + Index.ToString("0000");
            XElement MyXml = new XElement("Property");
            MyXml.SetAttributeValue("Value", MyReader[MyField]);
            MyXml.SetAttributeValue("Field", MyField);
            Result += MyXml.ToString();
        }
        Brojac++;
        Result += "</Root>";
    }

    MyTimer.Stop();
    MyReader.Close();

    TimeSpan ts = MyTimer.Elapsed;

    //TIPS_AND_TRICKS: How to format and display the TimeSpan value.
    string elapsedTime = String.Format
                         ("{0:00}:{1:00}:{2:00}.{3:00}", 
                          ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);

    string Buffer = "Processing time: " + 
                     elapsedTime + "; Through-put: " + 
                     Convert.ToInt32(Brojac / ts.TotalSeconds).ToString() + 
                     " records per second; Total " +
                     p_Bucket.ToString("000000 ") + " records";
    Poruka(Buffer);
}

所以这里最大的问题是当我们运行时:

1 thread for 50.000 rows; processing time is 00:00:05.49; On average 9099 records per second;

4 threads for 50.000 rows (12.5k per thread); average processing time per thread is 00:00:20.80; On average 2390 records per second;

我的问题是,为什么我们使用更多线程和任务后,平均处理时间和行数会减少?难道他们不应该并行处理并在几毫秒内吞噬这个数据集吗?

【问题讨论】:

  • 可能是您的字符串连接消耗了“额外”时间。我的建议是使用 StringBuilder 对象并重复测量.. 但它有很多代码,这只是一个想法。
  • 您能否在一个更简单、可验证的示例中重现此行为(请参阅minimal reproducible example)?它的方式取决于太多的实现细节并且很难分析。我相信存在某种锁定,阻止您的任务尽可能快地进行。你真的应该让自己成为一个分析器并进行根本原因分析,因为我们能告诉你的一切都是猜测。
  • 那么时间去哪儿了?你测量了什么?您是否通过分析器运行代码?猜测代码在哪里运行缓慢是开发人员非常不擅长的事情。测量,测量,测量,然后当它没有意义时,回到我们这里。
  • 一个线程生成任务,它异步执行同步任务,哦,天哪。首先,考虑将其写成Parallel.ForEach 或并行LINQ .AsParallel();这已经比手动切割要简单得多。其次,不要立即进行并行处理,而是看看你在做什么以及它是否已经不能在 single 线程上加速。对于初学者来说,DataTable 是一个非常低效的类,因为它处理非类型化数据。其次,流数据通常比一次对所有内容进行大量转换更有效。
  • Result += MyXml.ToString(); 在许多附加的情况下会让你付出代价,但这可能不是你最大的问题。

标签: c# multithreading task


【解决方案1】:

正如 cmets 中所说,您的应用程序的主要瓶颈是垃圾收集器,因为您要进行大量分配。

使用 dotTrace 的分析会话显示 30% 的时间花费在 GC 中。这是巨大的

分配的主要来源是调用MyXml.ToString() 时分配的XmlWriter。如果性能是一个问题,您应该考虑生成 XML 的其他方法。例如,将其替换为 string.Format 会在我的机器上显示 5 倍的性能提升。

【讨论】:

  • 谢谢大佬!!!用string.Format 替换它实际上确实提高了速度和整体流程!
猜你喜欢
  • 1970-01-01
  • 2020-08-15
  • 2020-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-05
相关资源
最近更新 更多