我想达到以下要求;请提出一些解决方案。
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 :(得分:16)
答案 1 :(得分:4)
这将在.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);
}
}
}
答案 2 :(得分:2)
看看Joe Albahari的Producer/Consumer Queue example。它应该为你想要完成的事情提供一个良好的起点。
答案 3 :(得分: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.
}
如果您在应用程序的其他位置使用ThreadPool,那么这不是一个好的解决方案,因为它在您的应用程序中共享。
您还可以获取不同的线程池实现,例如SmartThreadPool
答案 4 :(得分: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
}
第一个会表现得更好,因为您没有为每个处理的文件名启动和停止线程的开销。第二个是更短更清洁,并充分利用线程池。可能你不会注意到性能差异。
答案 5 :(得分:0)
可以使用ParallelOptions设置最大线程
答案 6 :(得分: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;
}