如何在For循环中使用多线程

时间:2012-12-23 13:40:21

标签: c# .net multithreading .net-2.0

我想达到以下要求;请提出一些解决方案。

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循环之前处理所有文件。

请建议最佳方法。

7 个答案:

答案 0 :(得分:16)

尝试

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

MSDN

只有.Net 4才可用。希望可以接受。

答案 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)

答案 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;
}