C# 多个线程将其结果填充到一个DataTable C中#

C# 多个线程将其结果填充到一个DataTable C中#,c#,multithreading,datatable,C#,Multithreading,Datatable,我刚刚开始学习线程的概念,我有点被一个问题困住了,它让我发疯 我真正需要完成的- 我在一个本地目录中有大约300个文本文件,需要对它们进行特定值的解析。。。在每个文本文件中找到这些“值”后,我需要将它们存储在数据库中。。 因此,我遵循访问目录中每个文本文件的简单方法—解析结果值并将其作为一行更新到本地DataTable,当我解析完所有文件并将300行存储到DataTable时,我会将DataTable的SQLBulkCopy复制到数据库中。这种方法工作得很好,只是运行代码大约需要10分钟 我现在

我刚刚开始学习线程的概念,我有点被一个问题困住了,它让我发疯

我真正需要完成的-

我在一个本地目录中有大约300个文本文件,需要对它们进行特定值的解析。。。在每个文本文件中找到这些“值”后,我需要将它们存储在数据库中。。 因此,我遵循访问目录中每个文本文件的简单方法—解析结果值并将其作为一行更新到本地DataTable,当我解析完所有文件并将300行存储到DataTable时,我会将DataTable的SQLBulkCopy复制到数据库中。这种方法工作得很好,只是运行代码大约需要10分钟

我现在想做的是什么-

为每个文件创建一个新线程,并在任何给定时间将线程数保持在4以下。。。然后每个线程将解析文件并返回一行以更新本地数据表

我被卡住了-我不知道如何更新这个从多个线程获取行的单个数据表

这是一个很好的解释,不是吗。。希望这里有人能提出一个好主意

谢谢,
Nidhi

如果您只让四个线程中的每一个线程自己写入数据库,那么这将容易得多。在这种情况下,您不必担心线程问题(每个线程处理的文件除外),因为每个工作线程都可以维护自己的数据表并消耗25%的文件

或者,您可以拥有一个所有线程都使用的datatable——只需确保使用锁包装对它的访问,如下所示:

lock(YourTable.Rows.SyncRoot){
  // add rows to table
}


当然,正如@David B所指出的,如果瓶颈是磁盘,那么这一切都是没有意义的。

SQLBulkCopy是一个只有300行的大锤子


退房。这是一个实例线程池,您可以很容易地将其限制为4个线程。因为只有300行考虑将它们直接发送到每个线程中的SQL,而不是在代码中聚合。

正如其他人指出的,请记住在更新之前锁定表。 C#:


至于实际控制和保持线程对齐的方法,只需使用ThreadPool对象,将最大线程数设置为您的限制,队列就可以解决问题。对于额外的控制,您可以加入一些使用WaitHandle对象数组的逻辑。事实上,考虑到您想要将300个单独的对象排队,这实际上可能是一个好主意。

是什么让您认为更多线程可以改善情况?他们可能不会


我建议你先让程序运行起来,然后再考虑让它运行得更快。只使用一个线程。

正如有人指出的,您需要准确地检查瓶颈在哪里以及为什么要使用线程

通过移动到多线程,您确实有可能提高性能。但是,如果要用每个线程更新相同的DataTable,则会受到DataTable的限制。一次只能有一个线程写入数据表(用锁控制),因此基本上仍在按顺序处理

另一方面,大多数数据库都是为多个连接设计的,运行在多个线程上,并为此进行了高度调优。如果仍要使用多个线程:让每个线程都有自己的数据库连接,并执行自己的处理

现在,根据正在进行的处理类型,您的瓶颈可能是打开和处理文件,而不是数据库更新

分割事物的一种方法:

  • 将所有要处理的文件名放入文件名队列
  • 创建一个线程(或多个线程)以从文件名队列中提取一个项目,打开并解析和处理该文件,并将结果推送到结果队列中
  • 让另一个线程从结果队列中获取结果,并将其插入数据库
  • 这些可以同时运行。。。在有东西要更新之前,数据库不会更新,只会在此期间等待

    这种方法让你真正知道谁在等谁。如果读取/处理文件部分速度较慢,请创建更多线程来执行此操作。如果“插入到数据库”部分速度较慢,请创建更多线程来执行此操作。队列只需要同步

    所以,伪代码:

    Queue<string> _filesToProcess = new Queue<string>();
    Queue<string> _results = new Queue<string>();
    Thread _fileProcessingThread = new Thread( ProcessFiles );
    Thread _databaseUpdatingThread = new Thread( UpdateDatabase );
    bool _finished = false;
    
    static void Main()
    {
        foreach( string fileName in GetFileNamesToProcess() )
        {
           _filesToProcess.Enqueue( fileName );
        }
    
        _fileProcessingThread.Start();
        _databaseUpdatingThread.Start();
    
        // if we want to wait until they're both finished
        _fileProcessingThread.Join();
        _databaseUpdatingThread.Join();
    
        Console.WriteLine( "Done" );
    }
    
    void ProcessFiles()
    {
       bool filesLeft = true;
    
       lock( _filesToProcess ){ filesLeft = _filesToProcess.Count() > 0; }
    
       while( filesLeft )
       {
          string fileToProcess;
          lock( _filesToProcess ){ fileToProcess = _filesToProcess.Dequeue(); }
    
          string resultAsString = ProcessFileAndGetResult( fileToProcess );
    
          lock( _results ){ _results.Enqueue( resultAsString ); }
    
          Thread.Sleep(1); // prevent the CPU from being 100%
    
          lock( _filesToProcess ){ filesLeft = _filesToProcess.Count() > 0; }
       }
    
       _finished = true;
    }
    
    void UpdateDatabase()
    {
       bool pendingResults = false;
    
       lock( _results ){ pendingResults = _results.Count() > 0; }
    
       while( !_finished || pendingResults )
       {
          if( pendingResults )
          {
             string resultsAsString;
             lock( _results ){ resultsAsString = _results.Dequeue(); }
    
             InsertIntoDatabase( resultsAsString ); // implement this however
          }
    
          Thread.Sleep( 1 ); // prevents the CPU usage from being 100%
    
          lock( _results ){ pendingResults = _results.Count() > 0; }
       }
    }
    
    Queue\u filesToProcess=new Queue();
    队列_结果=新队列();
    线程_fileProcessingThread=新线程(ProcessFiles);
    Thread _DatabaseUpdatenThread=新线程(UpdateDatabase);
    bool_finished=false;
    静态void Main()
    {
    foreach(GetFileNamesToProcess()中的字符串文件名)
    {
    _filesToProcess.Enqueue(文件名);
    }
    _fileProcessingThread.Start();
    _DatabaseUpdatengThread.Start();
    //如果我们想等到他们都完成了
    _fileProcessingThread.Join();
    _DatabaseUpdatengThread.Join();
    控制台。写入线(“完成”);
    }
    void ProcessFiles()
    {
    bool filesleet=true;
    lock(_filesToProcess){filesleet=_filesToProcess.Count()>0;}
    while(左)
    {
    字符串文件处理;
    lock(_filesToProcess){fileToProcess=_filesToProcess.Dequeue();}
    string resultAsString=ProcessFileAndGetResult(fileToProcess);
    锁(_results){_results.Enqueue(resultAsString);}
    Thread.Sleep(1);//防止CPU被100%
    lock(_filesToProcess){filesleet=_filesToProcess.Count()>0;}
    }
    _完成=正确;
    }
    void UpdateDatabase()
    {
    bool pendingreults=false;
    锁(_results){pendingreults=_results.Count()>0;}
    而(!|u完成|待处理结果)
    {
    如果(未决结果)
    {
    字符串结果字符串;
    锁(_results){resultsAsString=_results.Dequeue();}
    InsertIntoDatabase(resultsAsString);//但是要实现这个
    }
    Thread.Sleep(1);//防止CPU使用率达到100%
    锁定(_结果){pendingRe
    
    Queue<string> _filesToProcess = new Queue<string>();
    Queue<string> _results = new Queue<string>();
    Thread _fileProcessingThread = new Thread( ProcessFiles );
    Thread _databaseUpdatingThread = new Thread( UpdateDatabase );
    bool _finished = false;
    
    static void Main()
    {
        foreach( string fileName in GetFileNamesToProcess() )
        {
           _filesToProcess.Enqueue( fileName );
        }
    
        _fileProcessingThread.Start();
        _databaseUpdatingThread.Start();
    
        // if we want to wait until they're both finished
        _fileProcessingThread.Join();
        _databaseUpdatingThread.Join();
    
        Console.WriteLine( "Done" );
    }
    
    void ProcessFiles()
    {
       bool filesLeft = true;
    
       lock( _filesToProcess ){ filesLeft = _filesToProcess.Count() > 0; }
    
       while( filesLeft )
       {
          string fileToProcess;
          lock( _filesToProcess ){ fileToProcess = _filesToProcess.Dequeue(); }
    
          string resultAsString = ProcessFileAndGetResult( fileToProcess );
    
          lock( _results ){ _results.Enqueue( resultAsString ); }
    
          Thread.Sleep(1); // prevent the CPU from being 100%
    
          lock( _filesToProcess ){ filesLeft = _filesToProcess.Count() > 0; }
       }
    
       _finished = true;
    }
    
    void UpdateDatabase()
    {
       bool pendingResults = false;
    
       lock( _results ){ pendingResults = _results.Count() > 0; }
    
       while( !_finished || pendingResults )
       {
          if( pendingResults )
          {
             string resultsAsString;
             lock( _results ){ resultsAsString = _results.Dequeue(); }
    
             InsertIntoDatabase( resultsAsString ); // implement this however
          }
    
          Thread.Sleep( 1 ); // prevents the CPU usage from being 100%
    
          lock( _results ){ pendingResults = _results.Count() > 0; }
       }
    }