2010-07-20 3 views
4

모든 파일과 폴더를 반복하고 특정 확장명을 가진 모든 파일에 대해 작업을 수행하려고합니다. 이 방법은 잘 작동하지만, 수만개 이상의 파일을 처리 할 때 속도가 느리고 멀티 스레드를 사용하는 이미징을 사용하면 작업 속도가 빨라지기 때문에 다중 스레드로 만들고 싶습니다. 나는이 경우 스레딩을 사용하는 방법에 대해서 확신 할 수 없다.C#의 다중 스레드 디렉터리 반복

doStuff 속성 (날짜는 파일에서 등을 수정 읽고. 나는 그것을 할 수있는 그이 많이 최적화 있도록 검색 방법 호출하기 전에 트랜잭션을 시작하고 SQLite는 데이터베이스에 삽입합니다.

그것은 전체 작업 코드 답만큼 좋은 수행하는 방법에 대한 이론을 제공하는 답변.

private static string[] validTypes = { ".x", ".y", ".z", ".etc" }; 
    public static void scan(string rootDirectory) 
    { 
     try 
     { 

      foreach (string dir in Directory.GetDirectories(rootDirectory)) 
      { 

       if (dir.ToLower().IndexOf("$recycle.bin") == -1) 
        scan(dir); 
      } 

      foreach (string file in Directory.GetFiles(rootDirectory)) 
      { 

       if (!((IList<string>)validTypes).Contains(Path.GetExtension(file))) 
       { 
        continue; 
       } 


       doStuff(file); 
      } 
     } 
     catch (Exception) 
     { 
     } 
    } 
+0

이것은 주제와는 거리가 있지만 모든 예외를 포착해서는 안됩니다. –

+0

예, 동의합니다. 그 부분을 제거했습니다 (이것은 winforms 응용 프로그램이기 때문에). –

+3

왜 멀티 스레드가 어떤 것을 가속화 할 것이라고 생각합니까? ** 쓰레드는 마술처럼 빠르게 디스크를 실행시키지 않습니다. ** 디스크 컨트롤러는 이제 더 많은 일을 동시에 할 수 있기 때문에 쓰레드는 디스크를 더 느리게 실행할 수 있습니다. 왜 멀티 스레드 솔루션이 더 빠를 것이라고 생각하는지 설명 할 수 있습니까? –

답변

5

는 스레드 안전하고 완료하기 위해 전체 스캔을 기다릴 필요가 없습니다, 당신은이 같은 ThreadPool이에 모두 doStuffscan, 호출 할 수

string path = file; 
ThreadPool.QueueUserWorkItem(delegate { doStuff(path); }); 

당신을 익명 메소드가 file 변수 자체를 캡처하고 루프 전체에서 변경 사항을 볼 수 있으므로 별도의 로컬 변수를 만들어야합니다. (즉, ThreadPool이 루프가 다음 파일로 계속 된 후에 만 ​​작업을 실행하면 잘못된 파일이 처리됩니다.)

그러나 여기에서 주요 문제는 디스크 IO입니다. 멀티 스레딩은별로 도움이되지 않습니다.

Directory.GetFiles은 많은 수의 파일이있는 디렉토리에서 느리게 수행됩니다. (파일 이름을 저장하기 위해 배열을 할당해야하기 때문에) .Net 4.0을 사용하는 경우 EnumerateFiles method을 호출하는 것이 더 빠릅니다. 이터레이터를 사용하여 IEnumerable<string>을 반환하는 이터레이터를 사용합니다 루프를 실행하십시오.
당신은이처럼 SearchOption 매개 변수를 전달하여 방법 중 하나와 재귀 scan 호출을 피할 수 있습니다 : 단일 foreach 루프를하기 만합니다 그래서

foreach (string file in Directory.EnumerateFiles(rootDirectory, "*", SearchOption.AllDirectories)) 

이 반복적으로 모든 하위 디렉토리를 검색합니다.
성능 문제가 GetFiles으로 악화되므로 pre -.Net 4.0을 피할 수 있습니다.

+0

이것이 올바른 방법이지만 여러 스레드가 검색을 수행 할 수도 있습니다. –

+0

@ 스벤 : 이미 말했습니다. (첫 번째 단락에서) – SLaks

+0

파일을 읽는 것만으로도 스레딩이 도움이되지 않습니까? 나는 이것을 시도하는 것이 가치가 있다고 생각한다. –

1

그들은 매우 CPU를 많이 아니라면 정확히 doStuffscan은?합니까 어떻게 내가 생각했을 그 디스크 액세스 병목이 될 것이고, 멀티 스레드를 만들면 느려질 수 있습니다. doStuff한다고 가정

+0

'doStuff'는 파일에서 속성 (날짜 수정 등)을 읽고이를 sqlite 데이터베이스에 삽입합니다. 스캔 방법이 호출되기 전에 트랜잭션을 시작하므로 최대한 최적화되었습니다. –

+0

@Ramblingwood : 처음에는 2 개의 스레드 만 있으면 모든 파일 세부 정보를 메모리로 읽고 다른 하나는 정보를 사용하여 DB에 쓸 수 있습니다. 그런 다음 각각의 처리 시간을 측정하고 올바른 작업을 최적화 할 수 있습니다. –

+0

좋은 생각처럼 들리 네요. 나는 그것을 시도 할 것이다. –

2

IO 작업에서 멀티 스레딩을 사용하는 것은 일반적으로 나쁜 호출입니다 *. 여러 CPU 또는 여러 코어가있는 CPU가있을 수 있습니다. 하지만 일반적으로 하드 디스크는 동시에 여러 파일을 읽거나 쓸 수 없습니다.. 이런 종류의 일은 일반적으로 연재 될 필요가 있습니다.

즉, UI 스레드와 별도의 스레드에서 이러한 종류의 작업을 수행하는 것이 좋습니다. 그렇게하면 앱이 힘든 일을하는 동안 UI가 반응을 유지합니다.

* scandoStuff 메서드가 실제로 하드 디스크에 데이터를 읽거나 쓰는 것으로 가정합니다.그렇지 않은 경우이 코드를 병렬 처리하면 결국 의미가 있습니다.

+0

나는 다행스럽게도 독서 만하는 것이 아니다. –

+0

@Ramblingwood : 파일의 * 내용 *을 읽거나 경로 및/또는'DirectoryInfo' /'FileInfo' 객체의 속성을보고 있습니까? 하드 디스크에서 읽기는 대부분의 시스템에서 다중 스레드를 수행 할 수도 없습니다. –

+0

알겠습니다. 나는 미래에 실제로 하드 디스크에서 읽으려고 확장하기를 원했기 때문에 이것을 다중 스레드해서는 안된다. –

1

배열은 IEnumerable<T>을 .net 3.5+에서 구현하기 때문에 validTypesIList<string>으로 변환 할 필요가 없습니다.

두 번째로 validTypesHashSet으로 더 잘 구현되어 O (n) 대신 O (1) 조회를 Contains과 함께 제공 할 수 있습니다. 즉,이 경우 응용 프로그램이 IO 바인딩이므로 다른 응답에서 지적한 것처럼 성능에 영향을주지 않을 것입니다.

0

답변 해 주신 모든 분들께 감사드립니다. 내가 함께가는 결국은

 foreach (string file in Directory.EnumerateFiles(rootDirectory, "*", SearchOption.AllDirectories)) 
     { 
      if (!((IList<string>)validTypes).Contains(Path.GetExtension(file))) 
      { 
       continue; 
      } 
      string path = file; 
      ThreadPool.QueueUserWorkItem(delegate { doStuff(path); }); 
     } 

는 전에 복용했다는 여러 시간에 비해 약 2 분 안에 실행했다. 지연의 대부분은 파일 IO가 아니라 데이터베이스에 있다고 생각합니다.

감사합니다.

+0

나는 당신을 위해 잘 된 것을 기쁘게 생각합니다. –