C# 循环文件夹中的文件并将每个文件添加到线程,但一次只运行一个线程

C# 循环文件夹中的文件并将每个文件添加到线程,但一次只运行一个线程,c#,multithreading,C#,Multithreading,所以我有一个文件夹,里面有几个文件。我有一个循环,它将遍历每个文件,并将其添加到后台处理的线程中,以便UI响应。问题是我希望在给定的时间只运行一个线程。所以基本上我想“排队”线程,当一个线程完成时,触发下一个线程。最好的方法是什么?这是我正在使用的代码。我想知道计时器是否是最好的解决方案?谢谢大家 foreach (CustomerFile f in CF) { btnGo.Enabled = false; UpdateProgressDelegate showProgress

所以我有一个文件夹,里面有几个文件。我有一个循环,它将遍历每个文件,并将其添加到后台处理的线程中,以便UI响应。问题是我希望在给定的时间只运行一个线程。所以基本上我想“排队”线程,当一个线程完成时,触发下一个线程。最好的方法是什么?这是我正在使用的代码。我想知道计时器是否是最好的解决方案?谢谢大家

foreach (CustomerFile f in CF)
{
    btnGo.Enabled = false;
    UpdateProgressDelegate showProgress = new UpdateProgressDelegate(UpdateProgress);
    ProcessFile pf = new ProcessFile(this, showProgress, f._FileName, txtDestFolder.Text);
    Thread t = new Thread(new ThreadStart(pf.DoWork));
    t.IsBackground = true; 
    t.Start();
}

将文件添加到队列并在另一个线程上处理该队列如何

Queue<CustomerFile> files = new Queue<CustomerFile>()
foreach (CustomerFile f in CF)
  files.Enqueue(f);

BackgroundWorker bwk = new BackgroundWorker();
bwk.DoWork+=()=>{
    //Process the queue here
    // if you update the UI don't forget to call that on the UI thread
};

bwk.RunWorkerAsync();
队列文件=新队列()
foreach(CF中的CustomerFile f)
文件排队(f);
BackgroundWorker bwk=新的BackgroundWorker();
bwk.DoWork+=()=>{
//在这里处理队列
//如果你更新了UI,别忘了在UI线程上调用它
};
bwk.RunWorkerAsync();

将文件添加到队列并在另一个线程上处理该队列如何

Queue<CustomerFile> files = new Queue<CustomerFile>()
foreach (CustomerFile f in CF)
  files.Enqueue(f);

BackgroundWorker bwk = new BackgroundWorker();
bwk.DoWork+=()=>{
    //Process the queue here
    // if you update the UI don't forget to call that on the UI thread
};

bwk.RunWorkerAsync();
队列文件=新队列()
foreach(CF中的CustomerFile f)
文件排队(f);
BackgroundWorker bwk=新的BackgroundWorker();
bwk.DoWork+=()=>{
//在这里处理队列
//如果你更新了UI,别忘了在UI线程上调用它
};
bwk.RunWorkerAsync();

这是生产者-消费者模型,这是一个非常常见的需求。在C#中,
BlockingCollection
非常适合此任务。让制作人向该集合添加项目,然后让后台任务(可以有任意数量的项目)从该集合中获取项目。

这是制作人-消费者模型,这是一个非常常见的要求。在C#中,
BlockingCollection
非常适合此任务。让制作人将项目添加到该集合中,然后让后台任务(您可以有任意数量的项目)从该集合中获取项目。

听起来您只需要一个处理队列的后台线程即可。大概是这样的:

var q = new Queue();
foreach (var file in Directory.GetFiles("path"))
{
    q.Enqueue(file);
}

var t = new Task(() =>
    {
        while (q.Count > 0)
        {
            ProcessFile(q.Dequeue());
        }
    });
t.Start();
new Thread(()=>{
foreach (CustomerFile f in CF)
{
    btnGo.Enabled = false;
    UpdateProgressDelegate showProgress = new UpdateProgressDelegate(UpdateProgress);
    ProcessFile pf = new ProcessFile(this, showProgress, f._FileName, txtDestFolder.Text);
    pf.DoWork();

}
}).Start();

请注意,只有在后台线程处理队列时不必修改队列时,这才合适。如果你这样做了,Servy的答案是正确的:这是一个相当标准的生产者-消费者问题,只有一个消费者。有关解决生产者/消费者问题的更多信息,请参阅Albahari的。

听起来您只需要一个处理队列的后台线程就可以解决问题。大概是这样的:

var q = new Queue();
foreach (var file in Directory.GetFiles("path"))
{
    q.Enqueue(file);
}

var t = new Task(() =>
    {
        while (q.Count > 0)
        {
            ProcessFile(q.Dequeue());
        }
    });
t.Start();
new Thread(()=>{
foreach (CustomerFile f in CF)
{
    btnGo.Enabled = false;
    UpdateProgressDelegate showProgress = new UpdateProgressDelegate(UpdateProgress);
    ProcessFile pf = new ProcessFile(this, showProgress, f._FileName, txtDestFolder.Text);
    pf.DoWork();

}
}).Start();

请注意,只有在后台线程处理队列时不必修改队列时,这才合适。如果你这样做了,Servy的答案是正确的:这是一个相当标准的生产者-消费者问题,只有一个消费者。有关解决生产者/消费者问题的更多信息,请参阅Albahari的。

您必须做的唯一一件事是将循环放入线程中,例如:

var q = new Queue();
foreach (var file in Directory.GetFiles("path"))
{
    q.Enqueue(file);
}

var t = new Task(() =>
    {
        while (q.Count > 0)
        {
            ProcessFile(q.Dequeue());
        }
    });
t.Start();
new Thread(()=>{
foreach (CustomerFile f in CF)
{
    btnGo.Enabled = false;
    UpdateProgressDelegate showProgress = new UpdateProgressDelegate(UpdateProgress);
    ProcessFile pf = new ProcessFile(this, showProgress, f._FileName, txtDestFolder.Text);
    pf.DoWork();

}
}).Start();

您必须做的唯一一件事是将循环放入线程中,例如:

var q = new Queue();
foreach (var file in Directory.GetFiles("path"))
{
    q.Enqueue(file);
}

var t = new Task(() =>
    {
        while (q.Count > 0)
        {
            ProcessFile(q.Dequeue());
        }
    });
t.Start();
new Thread(()=>{
foreach (CustomerFile f in CF)
{
    btnGo.Enabled = false;
    UpdateProgressDelegate showProgress = new UpdateProgressDelegate(UpdateProgress);
    ProcessFile pf = new ProcessFile(this, showProgress, f._FileName, txtDestFolder.Text);
    pf.DoWork();

}
}).Start();

请为此添加语言标记。如果要创建多个线程,但一次只运行一个线程,则选择了错误的工具。线程适用于当您确实希望并行执行(可能-不保证)时,我只会使用C#
async
,而不是手动生成线程。另外,如果一次只处理1个线程,为什么不重用同一个线程呢?创建线程是一个相对密集的过程,需要消耗大约1MB的RAM。我不认为扔掉它并为下一个文件创建另一个文件有什么意义。如果您只是想让UI保持响应性或其他功能,请使用1个线程以正常循环方式处理文件,或使用
async
。只需启动一个线程并将CustomerFile集合交给它。请为此添加语言标记。如果您想创建多个线程,但一次只想运行一个线程,你选错工具了。线程适用于当您确实希望并行执行(可能-不保证)时,我只会使用C#
async
,而不是手动生成线程。另外,如果一次只处理1个线程,为什么不重用同一个线程呢?创建线程是一个相对密集的过程,需要消耗大约1MB的RAM。我不认为扔掉它并为下一个文件创建另一个文件有什么意义。如果您只是想让UI保持响应性,请使用1个线程以正常循环方式处理文件,或者使用
async
。只需启动一个线程并将一个CustomerFile集合交给它即可。
Queue
不是线程保存;因此,您需要同步访问。@Servy-如果原始线程只是填充队列(如图所示),而后台工作线程是唯一退出队列的线程(并且在队列完成后启动),则无需同步。
queue
不是线程保存;因此,您需要同步访问。@Servy-如果原始线程只是填充队列(如图所示),而后台工作线程是唯一退出队列的线程(并且在队列完成后启动),则无需同步。+1。在这种情况下,没有理由创建多个线程。+1。在这种情况下,没有理由创建多个线程。