2009-06-12 9 views
3

난 그냥 스레딩의 개념을 배우기 시작하고있어, 나는 종류의 한 가지 문제에 붙어있어, 그 미친 날 운전 ....다중 스레드 #

무엇 실제로 달성해야합니다 -

로컬 디렉토리에 특정 값에 대해 구문 분석해야하는 300 개의 텍스트 파일이 있습니다 ... 각 텍스트 파일에서이 "값"을 찾은 후이를 저장해야합니다. 데이터베이스 .. 그래서 디렉터리의 각 텍스트 파일에 액세스하는 일반 접근법을 따랐습니다. 파싱하고 결과 값을 로컬 DataTable에 대한 행으로 업데이트하고, 모든 파일을 구문 분석하고 300 행을 DataTable, 나는 내 데이터베이스에 DataTable의 SQLBulkCopy. 이 접근법은 코드를 실행하는 데 약 10 분이 소요된다는 것을 제외하고는 잘 작동합니다!

내가 지금 시도하고있는 무슨 -

각 파일에 대한 새 스레드를 생성하고 주어진 시간에 4 아래의 스레드 수를 유지 ... 각 스레드는 파일을 구문 분석하고 행을 반환 내가 붙어있어 지역 DataTable을

을 업데이트 - 나는 꽤 설명이 그렇지 않은 여러 스레드에서 행을 얻을이 하나의 DataTable을 ...

를 업데이트하는 방법을 이해하지 않습니다 .. 일부 희망 하나는 여기에 좋은 아이디어를 제안 할 수 있습니다 ...

고마워, Nidhi

+0

여러 스레드가 제한된 리소스 (디스크 액세스)를 더 풍부하게 만드는 이유는 무엇입니까? –

답변

4

4 개의 스레드 각각을 데이터베이스에 직접 쓰면 훨씬 쉬울 것입니다. 이 시나리오에서는 각 작업자 스레드가 자신의 데이터 테이블을 유지하고 파일의 25 %를 소비하므로 스레딩 (각 스레드가 작동하는 파일 제외)에 대해 걱정할 필요가 없습니다. 이 경우 모든 이론에 불과 물론

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

: -

또한, 모든 스레드가 사용하는 단일 데이터 테이블 수 있습니다 단지 포장해야합니다 그렇게 같은 잠금과에 액세스 @David B 메모처럼 병목 현상이 디스크입니다.

0

SQLBulkCopy는 단지 300 행에 대한 큰 해머입니다.

Smart Thread Pool을 확인하십시오. 이것은 매우 쉽게 4 개의 스레드로 제한 할 수있는 인스턴스 스레드 풀입니다. 당신은 단지 당신이 코드를 집계하기보다는 각 쓰레드에서 SQL에 직접 게시하는 것을 고려하고 있기 때문에 300 행이 있기 때문입니다.

+0

동의. 300 행은 아무것도 아닙니다. 연결 풀링과 가벼운 쿼리를 사용하면 한 번에 하나씩 데이터베이스에 300 번 칠 수 있습니다. –

+0

신속한 답장을 보내 주셔서 감사합니다. 그래서 "SQLBulkCopy는 단지 300 개의 행에 큰 해머입니다."미래에 어떤 상황에서 SQLBulkCopy를 사용할 수 있습니까? 1000 또는 10000 또는 행 수가 몇 개인지 ... 우리가 10 개의 열을 가지고 있다고 가정 할 때 데이터베이스에 마지막 행을 삽입하는 각 스레드의 조언을 따릅니다. ... 내 문제를 해결 ... – Nidhi

+0

하지만 모든 스레드가 단일 DataTable에보고하는 경우에 datatable을 업데이트하는 방법을 여전히 모르겠습니다. lock (YourTable.Rows.SyncRoot) { // } 정말하지 않습니다 테이블 } 또는 잠금 (myTable에) { myTable.AddnewRow (...)이 행을 추가 나를 많이 도와 줘, 누구든지 좋은 샘플 코드를 연결시켜 줄 수 있니? – Nidhi

0

다른 사람들이 지적했듯이 업데이트하기 전에 테이블을 잠 가야합니다. C 번호 : 실제로 제어 라인에 스레드를 유지하는 방법에 관해서는

private object tableLock; 

/* 
Later in code. 
*/ 

private void UpdateDataTable(object data) 
{ 
    lock(tableLock) 
    { 
      //Add or update table rows 
    } 
} 

, 그냥, 스레드 풀 개체를 사용하여 제한 할 수있는 최대 스레드를 설정하고 대기 것들을 돌볼 수 있습니다. 추가 제어를 위해 WaitHandle 객체 배열을 사용하는 일부 로직을 던질 수 있습니다. 실제로 이것은 300 개의 개별 객체를 대기열에 넣으 려 할 때 실제로는 좋은 아이디어 일 수 있습니다.

1

더 많은 스레드가 사물을 향상시킬 것이라고 생각하게 된 이유는 무엇입니까? 그들은 아마하지 않을 것이다.

먼저 프로그램을 작동 시키면 더 빨리 작동하도록 걱정하십시오. 하나의 스레드에서만 수행하십시오.

6

다소 지적 되었 듯이 병목 현상이 발생한 위치와 스레딩을 사용하는 이유를 정확하게 검토해야합니다.

여러 스레드로 이동하면 성능이 향상 될 수 있습니다. 그러나 각 스레드로 동일한 DataTable을 업데이트하는 경우 DataTable에 의해 제한됩니다. 한 번에 하나의 스레드 만 DataTable에 쓸 수 있습니다 (잠금으로 제어하는 ​​스레드). 그래서 기본적으로 순차적으로 처리됩니다.

반면에 대부분의 데이터베이스는 다중 연결을 위해 설계되었으며 여러 스레드에서 실행되며 그 목적으로 고도로 조정되었습니다. 여러 개의 스레드를 계속 사용하려면 각 스레드가 데이터베이스에 대한 고유 한 연결을 가지며 자체 처리를 수행하십시오.

이제 진행되는 처리의 종류에 따라 병목 현상이 데이터베이스를 업데이트하지 않고 파일을 열고 처리하는 중일 수 있습니다. 물건을 분할하는

한 가지 방법 :

  1. 모든 파일 이름을 넣어은 파일 이름 대기열로 처리한다.
  2. 파일 이름 큐에서 항목을 가져 오려면 스레드 (또는 스레드)를 만들고 파일을 열고 파싱 한 후 결과 큐로 결과를 푸시합니다.
  3. 다른 스레드가 결과 큐에서 결과를 가져 와서 데이터베이스에 삽입하십시오.

동시에 실행할 수 있습니다. 업데이트 할 항목이있을 때까지 데이터베이스가 업데이트되지 않고 그 동안 기다리게됩니다.

이 방법을 사용하면 누가 누구를 기다리고 있는지 알 수 있습니다. 읽기/처리 파일 부분이 느린 경우이를 수행 할 더 많은 스레드를 작성하십시오. 데이터베이스 파트 삽입이 느린 경우 추가 스레드를 만들어 해당 작업을 수행하십시오. 대기열은 동기화되어야합니다.

그래서, 의사 :

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()를 호출하십시오.

가장 간단한 예는 아니지만 철저한 것 같습니다. 두 개의 대기열을 동기화 중이므로 액세스하기 전에 각 대기열이 잠겨 있는지 확인해야합니다. 각 스레드가 완료되어야하는 시점을 추적하고 있으며 스레드간에 데이터를 마샬링하고 있지만 대기열을 사용하여 두 번 이상 처리하지 않았습니다.

희망이 있습니다.