2010-08-16 73 views
1

我有一个应用程序,我已经开始使用,似乎我需要重新思考一下。该应用程序目前是一个winform应用程序。无论如何,我允许用户输入他们想要运行的线程数量。我还允许用户分配每个线程处理的记录数。我所做的是循环线程数量变量并相应地创建线程。我没有在线程上执行任何锁定(并且不确定是否需要)。我是线程新手,并且遇到多核可能出现的问题。我需要一些建议来告诉我如何让这个表现更好。.NET多线程帮助

在创建线程之前,会从我的数据库中提取一些记录进行处理。该列表对象被发送到线程并循环播放。一旦到达循环结束,线程调用数据函数来拉取一些新记录,替换列表中的旧记录。这一直持续下去,直到没有更多的记录。这里是我的代码:

private void CreateThreads() 
{ 
    _startTime = DateTime.Now; 
    var totalThreads = 0; 
    var totalRecords = 0; 
    progressThreadsCreated.Maximum = _threadCount; 
    progressThreadsCreated.Step = 1; 
    LabelThreadsCreated.Text = "0/" + _threadCount.ToString(); 
    this.Update(); 
    for(var i = 1; i <= _threadCount; i++) 
    { 
     LabelThreadsCreated.Text = i + "/" + _threadCount; 
     progressThreadsCreated.Value = i; 
     var adapter = new Dystopia.DataAdapter(); 
     var records = adapter.FindAllWithLocking(_recordsPerThread,_validationId,_validationDateTime); 
     if(records != null && records.Count > 0) 
     { 
      totalThreads += 1; 
      LabelTotalProcesses.Text = "Total Processes Created: " + totalThreads.ToString(); 




      var paramss = new ArrayList { i, records }; 
      var thread = new Thread(new ParameterizedThreadStart(ThreadWorker)); 
      thread.Start(paramss); 
     } 

     this.Update(); 
    } 


} 


private void ThreadWorker(object paramList) 
{ 
    try 
    { 
     var parms = (ArrayList) paramList; 
     var stopThread = false; 
     var threadCount = (int) parms[0]; 
     var records = (List<Candidates>) parms[1]; 
     var runOnce = false; 
     var adapter = new Dystopia.DataAdapter(); 
     var lastCount = records.Count; 
     var runningCount = 0; 
     while (_stopThreads == false) 
     { 
      if (!runOnce) 
      { 
       CreateProgressArea(threadCount, records.Count); 
      } 
      else 
      { 
       ResetProgressBarMethod(threadCount, records.Count); 
      } 


      runOnce = true; 
      var counter = 0; 
      if (records.Count > 0) 
      { 
       foreach (var record in records) 
       { 
        counter += 1; 
        runningCount += 1; 
        _totalRecords += 1; 
        var rec = record; 
        var proc = new ProcRecords(); 
        proc.Validate(ref rec); 

        adapter.Update(rec); 

        UpdateProgressBarMethod(threadCount, counter, emails.Count, runningCount); 

        if (_stopThreads) 
        { 
         break; 
        } 
       } 

       UpdateProgressBarMethod(threadCount, -1, lastCount, runningCount); 

       if (!_noRecordsInPool) 
       { 
        records = adapter.FindAllWithLocking(_recordsPerThread, _validationId, _validationDateTime); 
        if (records == null || records.Count <= 0) 
        { 
         _noRecordsInPool = true; 
         break; 
        } 
        else 
        { 
         lastCount = records.Count; 
        } 
       } 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show(ex.Message); 
    } 
} 
+0

您遇到的问题是什么? 此外,我敢肯定你需要在你的异常catch MessageBox.Show中引发UI线程。我猜如果你的进度条工作,你已经这样做,但这是一个参考问题的想法(http://stackoverflow.com/questions/2367718/c-automating-the-invokerequired-code-pattern)。因为这个,你可能会失去你的例外吗? – 2010-08-16 01:20:06

+0

我正在使用代理来更新UI。这工作正常。尽管感谢您的反馈。 – DDiVita 2010-08-16 01:39:34

回答

3

我允许用户输入的线程数 他们想有 运行。我还允许用户将 分配给每个线程的记录数为 。

这不是你真正想要公开给用户的东西。他们应该放什么?他们如何确定什么是最好的?这是最好留给你的实现细节,或者更好的CLR或其他库。

我没有执行任何锁定(和 不知道我需要与否)的 线程。

大多数问题,你将与多线程将来自共享状态。具体来说,在您的ThreadWorker方法中,它看起来像您参考以下共享数据:_stopThreads,_totalRecords,_noRecordsInPool,_recordsPerThread,_validationId_validationDateTime

但是,仅仅因为这些数据是共享的,并不意味着您会遇到问题。这完全取决于谁读和写。例如,我认为_recordsPerThread最初只写入一次,然后被所有线程读取,这很好。然而,_totalRecords都是由每个线程读取和写入的。由于_totalRecords += 1;包含非原子读写然后可以在这里遇到线程问题。换句话说,你可以让两个线程读取_totalRecords的值(假设他们都读取值5),然后增加它们的副本,然后将其写回。他们都会写回值6,现在不正确,因为它应该是7.这是一个典型的race condition。对于这种特殊情况,您可以使用Interlocked.Increment自动更新该字段。通常,为了在C#中的线程之间进行同步,可以使用System.Threading命名空间中的类,例如, Mutex,Semaphore,并且可能是最常见的,Monitor(相当于lock),它只允许一个线程一次执行特定部分的代码。您用于同步的机制完全取决于您的性能要求。例如,如果您在ThreadWorker的身体周围掷出lock,您将通过有效地序列化工作来摧毁通过多线程获得的任何性能提升。安全,但速度缓慢:(在另一方面,如果你使用Interlocked.Increment和明智地添加其他同步在必要时,你可以保持你的表现您的应用程序将是正确的:)

一旦你已经得到了你的工人方法是线程安全的,你应该使用一些其他机制来管理你的线程。 ThreadPool被提及,你也可以使用Task Parallel Library,它摘要在ThreadPool上,巧妙地确定和缩放使用多少个线程。通过这种方式,您可以减轻用户的负担,以确定它们应该运行的线程数量。

0

显而易见的答案是质疑为什么你想在第一个地方线程?分析和基准表明使用线程将会是一个优势在哪里?

你如何确保non-gui线程不与gui交互?你如何确保没有两个线程以不安全的方式与相同的变量或数据结构交互?即使您意识到您确实需要使用锁定,您又如何确保锁定不会导致每个线程连续处理其工作负载,从而消除了多个线程可能提供的优势?

+0

我需要多个线程,因为我们正在验证记录并使网络连接来验证数据。我们正在处理我们数据库中的数百万条记录,只有一个流程可以做到这一点似乎并不实际。我们确实尝试了只使用一个线程,但是这个过程太慢了。 – DDiVita 2010-08-16 01:38:49

+1

听起来对我来说非常重要。 (通过连接来验证数据?)我仍然怀疑线程实际上会从这个描述中获得任何东西。 – Arafangion 2010-08-16 01:55:34

+0

在这种情况下是这样。谢谢 – DDiVita 2010-08-17 12:55:27

4

你可以做的一件简单的事情可以改善性能将使用ThreadPool来管理你的线程创建。这允许操作系统分配一组线程而不是多次支付线程创建惩罚。

如果您决定转移到.NET 4.0,Task将是另一种方式。

+0

嗨,很高兴在这里看到你:) – 2010-08-16 01:37:21

+0

这不是问题的答案 - 问题不在于如何管理线程池或优化线程数 - 而是关于更基本的问题首先如何使用线程。 人们在了解锁定以及多线程设计的其他真正基本方面之后,就可以了解线程池。 (换句话说,这是一个工具)。 – Arafangion 2010-08-16 04:04:44

+0

其实我回答了他的问题。非常精确。他问道“......我如何才能让这个表现更好。”尽管您对线程的更深入理解是重要的,但ThreadPool将使其性能更好。 – linuxuser27 2010-08-16 04:10:51