我尝试学习如何使用线程池和互斥锁,作为一种做法,我正在尝试创建一个应用程序,将文件从计算机中的一个路径复制到计算机中的另一个路径。 为了完成这个应用程序,我使用了线程池(因此可以同时进行一些复制):
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:..........',因为它被另一个进程使用“不应该出现。 正如我想的那样,异常没有出现,但是应用程序只将一个文件从源文件夹复制到目标文件夹而不是所有文件。 当我试图一步一步地运行这个应用程序时,发生了同样奇怪的事情并且一切正常,所有文件都被复制到目标文件夹。
为什么会这样?如何解决此问题,以便所有文件都将在正常运行的应用程序中复制到目标文件夹而不是一步一步?
答案 0 :(得分:2)
您的问题不是ThreadPool
。这个论点出了什么问题。
在第一个代码片段中,使用两个参数值填充对象数组,并将其传递给Queue方法。这里发生的是,你总是使用相同的数组。因此,在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);
}