Как уже указывалось, вам нужно точно определить, где находится ваше узкое место и почему вы используете многопоточность.
При переходе к нескольким потокам у вас есть потенциал для повышения производительности. Однако, если вы обновляете один и тот же DataTable для каждого потока, вы ограничены 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; }
}
}
Я почти уверен, что есть способы сделать это "лучше", но это должно помочь, чтобы вы могли читать и обрабатывать данные, а также добавлять завершенные данные в базу данных и использовать преимущества многопоточности.
Если вы хотите, чтобы другой поток обрабатывал файлы или обновлял базу данных, просто создайте новый поток (MethodName) и вызовите Start ().
Это не самый простой пример, но я думаю, что он тщательный. Вы синхронизируете две очереди, и вам необходимо убедиться, что каждая из них заблокирована перед доступом. Вы отслеживаете, когда должен завершиться каждый поток, и данные распределяются между потоками, но никогда не обрабатываются более одного раза с использованием очередей.
Надеюсь, это поможет.