2012-12-23 28 views
4

我想達到以下要求;請提出一些解決方案。如何在For循環中使用多線程

string[] filenames = Directory.GetFiles("C:\Temp"); //10 files 

for (int i = 0; i < filenames.count; i++)  
{ 
    ProcessFile(filenames[i]); //it takes time to execute  
} 

我想實現多線程。例如有10個文件。我想一次處理3個文件(可配置,例如maxthreadcount)。所以3個文件將在for循環中的3個線程中處理,並且如果任何線程完成執行,它應該從for循環中選擇下一個項目。還希望確保所有文件在退出for循環之前進行處理。

請建議最佳方法。

+0

你真的被困在.NET 2.0上嗎?在.NET 3.5中會有更好的方法,或者更好的4.0。 –

+0

@Joe:任何使用ThreadPool或Semaphore的建議? – SAM

+3

不,除非:忘記它,除非您的處理是CPU密集型的。光盤沒有變得更快。 IO將是一個嚴重的瓶頸問題。 – TomTom

回答

3

這將做的工作在.NET 2.0:

class Program 
{ 

    static int workingCounter = 0; 
    static int workingLimit = 10; 
    static int processedCounter = 0; 

    static void Main(string[] args) 
    { 
     string[] files = Directory.GetFiles("C:\\Temp"); 
     int checkCount = files.Length; 
     foreach (string file in files) 
     { 
      //wait for free limit... 
      while (workingCounter >= workingLimit) 
      { 
       Thread.Sleep(100); 
      } 
      workingCounter += 1; 
      ParameterizedThreadStart pts = new ParameterizedThreadStart(ProcessFile); 
      Thread th = new Thread(pts); 
      th.Start(file); 
     } 
     //wait for all threads to complete... 
     while (processedCounter< checkCount) 
     { 
      Thread.Sleep(100); 
     } 
     Console.WriteLine("Work completed!"); 
    } 

    static void ProcessFile(object file) 
    { 
     try 
     { 
      Console.WriteLine(DateTime.Now.ToString() + " recieved: " + file + " thread count is: " + workingCounter.ToString()); 
      //make some sleep for demo... 
      Thread.Sleep(2000); 
     } 
     catch (Exception ex) 
     { 
      //handle your exception... 
      string exMsg = ex.Message; 
     } 
     finally 
     { 
      Interlocked.Decrement(ref workingCounter); 
      Interlocked.Increment(ref processedCounter); 
     } 
    } 
} 
+1

如果你採取這種方法,一定要做適當的異常處理。如果你在異常情況下不減少'workingCounter',則會導致問題。 – CodingWithSpike

+2

遞減workingCounter有一個問題 - 這不是一個原子操作。兩個線程可能同時遞減,並且計數器只會遞減一次。最好使用Interlocked.Decrement(ref workingCounter)。 – ytoledano

+0

我已經合併了@CodingWithSpike和ytoledano的兩個建議,以使此示例更加健壯 –

11

嘗試

Parallel.For(0, filenames.Length, i => { 
    ProcessFile(filenames[i]); 
}); 

MSDN

這是唯一可用的,因爲.NET 4的希望是可以接受的。

+0

不幸的是,該應用程序的目標是2.0。任何其他建議? – SAM

1

你可以使用ThreadPool

例子:

ThreadPool.SetMaxThreads(3, 3); 

for (int i = 0; i < filenames.count; i++)  
{ 
    ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), filenames[i]); 
} 

static void ProcessFile(object fileNameObj) 
{ 
    var fileName = (string)fileNameObj; 
    // do your processing here. 
} 

如果您在別處使用線程池在應用程序中,然後,因爲它橫跨你的應用程序共享,這將不會是一個很好的解決方案。

你也可以抓住不同的線程池實現,例如SmartThreadPool

+1

)我將如何停止For循環在所有線程完成之前退出函數ProcessFile()? – SAM

0
var results = filenames.ToArray().AsParallel().Select(filename=>ProcessFile(filename)).ToArray(); 

bool ProcessFile(object fileNameObj) 
{ 
    var fileName = (string)fileNameObj; 

    // do your processing here. 

    return true; 
} 
+1

他說.NET 2.0。 –

1

而不是開始爲每個文件名一個線程,把文件名到一個隊列,然後啓動三個線程來處理它們。或者,由於主線程現在是免費的,啓動兩個線程,讓它的主線程工作,太:

Queue<string> MyQueue; 

void MyProc() 
{ 
    string[] filenames = Directory.GetFiles(...); 
    MyQueue = new Queue(filenames); 

    // start two threads 
    Thread t1 = new Thread((ThreadStart)ProcessQueue); 
    Thread t2 = new Thread((ThreadStart)ProcessQueue); 
    t1.Start(); 
    t2.Start(); 

    // main thread processes the queue, too! 
    ProcessQueue(); 

    // wait for threads to complete 
    t1.Join(); 
    t2.Join(); 
} 

private object queueLock = new object(); 

void ProcessQueue() 
{ 
    while (true) 
    { 
     string s; 
     lock (queueLock) 
     { 
      if (MyQueue.Count == 0) 
      { 
       // queue is empty 
       return; 
      } 
      s = MyQueue.Dequeue(); 
     } 
     ProcessFile(s); 
    } 
} 

另一種選擇是使用旗語來控制多少線程工作:

Semaphore MySem = new Semaphore(3, 3); 

void MyProc() 
{ 
    string[] filenames = Directory.GetFiles(...); 

    foreach (string s in filenames) 
    { 
     mySem.WaitOne(); 
     ThreadPool.QueueUserWorkItem(ProcessFile, s); 
    } 

    // wait for all threads to finish 
    int count = 0; 
    while (count < 3) 
    { 
     mySem.WaitOne(); 
     ++count; 
    } 
} 

void ProcessFile(object state) 
{ 
    string fname = (string)state; 
    // do whatever 
    mySem.Release(); // release so another thread can start 
} 

第一個執行會稍微好一些,因爲您沒有爲處理的每個文件名啓動和停止線程的開銷。然而,第二個更短,更清潔,並充分利用了線程池。可能你不會注意到性能差異。