gpt4 book ai didi

c# - 如何在 C# 中使用线程池和互斥量?

转载 作者:太空宇宙 更新时间:2023-11-03 10:25:35 26 4
gpt4 key购买 nike

我尝试学习如何使用线程池和互斥锁,作为一种练习,我正在尝试制作一个应用程序,将文件从计算机中的一个路径复制到计算机中的另一个路径。为了完成这个应用程序,我使用了线程池(因此可以同时发生几个副本):

object[] paths = new object [2];  // The source path and the destination path
string[] files[] = System.IO.Directory.GetFiles(sourceFolderPath); //string sourceFolderPath = Folder path that contains the files
foreach(string s in files)
{
paths[0] = s; // The file source - s = the file name with the file path;
paths[1] = s.Replace(sourceFolderPath, destFolderPath); // Replaces between the source folder and the destination folder but keeps the file name
ThreadPool.QueueUserWorkItem(new waitCallback(CopyFIle), paths);
}

到目前为止,应用程序将每个文件发送到将文件从源文件夹复制到目标文件夹的函数。

CopyFile 函数如下所示:

static void CopyFiles(object paths)
{
object[] temp = paths as object[]; // The casting to object array
string sourcePath = temp[0].ToString();
string destPath = temp[1].ToString();
System.IO.File.Copy(filePath, destPath, true); // The copy from the source to the dest
}

奇怪的是,当我运行该应用程序时,它抛出一个异常:“该进程无法访问文件 'C:............',因为它正被另一个进程使用”。当我试图找出错误并逐步运行应用程序时,应用程序运行正常,并且整个文件都从源文件夹复制到目标文件夹。

那个案例让我想到,也许我正在使用 ThreadPool 的事实使两个线程或更多线程打开同一个文件(这种情况不应该发生,因为我使用了 foreach 并且每个文件路径都被传输为一个参数只有一次)。

为了解决这个问题,我尝试使用 Mutex,现在 CopyFile 函数如下所示:

static Mutex mutex = new Mutex();
static void CopyFiles(object paths)
{
Mutex.WaitOne(); //Waits until the critical section is free from other threads
try
{
object[] temp = paths as object[]; // The casting to object array
string sourcePath = temp[0].ToString();
string destPath = temp[1].ToString();
System.IO.File.Copy(filePath, destPath, true); // The copy from the source to the dest
}
Finally
{
Mutex.ReleaseMutex(); //Release the critical area
}
}

现在应用程序应该等到关键区域空闲,然后才尝试复制文件,所以出现异常:“进程无法访问文件 'C:............' 因为它正在被另一个进程使用”不应该出现。正如我所想,没有出现异常,但应用程序仅将一个文件从源文件夹复制到目标文件夹,而不是所有文件。当我尝试逐步运行此应用程序时,同样奇怪的事情发生了,一切正常,所有文件都被复制到目标文件夹。

为什么会这样?我该如何解决这个问题,以便在正常运行的应用程序中将所有文件复制到目标文件夹,而不是逐步复制?

最佳答案

您的问题不在于 ThreadPool。问题出在论点上。

在您的第一个代码片段中,您使用两个参数值填充对象数组并将其传递给队列方法。这里发生的是,您始终使用相同的数组。因此,在 foreach 循环的第一次迭代中,您将两个值写入数组并传递给它。最终,该方法被执行为 ThreadPool,使用该对象数组。同时,在 foreach 循环的第二次迭代中,您再次写入该数组并将其再次传递给 ThreadPool。这意味着,两个(或更多)线程开始在该数组上工作。

您不知道 CopyFiles 方法何时处于事件状态,因此您无法判断阵列何时被拆箱并准备好重新使用。这可以使用互斥来实现(C# 中最简单的方法是使用 lock 关键字),但这不是您应该在此处使用的方式。

更好的方法是在 foreach 循环的每次迭代中创建新的对象数组。只需将代码更改为:

string[] files[] = System.IO.Directory.GetFiles(sourceFolderPath); //string sourceFolderPath = Folder path that contains the files
foreach(string s in files)
{
object[] paths = new object [2]; // The source path and the destination path
paths[0] = s; // The file source - s = the file name with the file path;
paths[1] = s.Replace(sourceFolderPath, destFolderPath); // Replaces between the source folder and the destination folder but keeps the file name
ThreadPool.QueueUserWorkItem(new waitCallback(CopyFIle), paths);
}

关于c# - 如何在 C# 中使用线程池和互斥量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31696357/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com