如何在 C# 中使用线程池和互斥量?
How to use Thread Pool and Mutex in c#?
我尝试学习如何使用线程池和互斥锁,作为一种练习,我正在尝试制作一个应用程序,将文件从计算机中的一个路径复制到计算机中的另一个路径。
为了完成这个应用程序,我使用了线程池(因此可以同时发生几个副本):
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
}
奇怪的是,当我 运行 应用程序抛出异常时:"The process cannot access to the file 'C:..........' because it is being used by another process"。
当我试图找出错误时,我 运行 应用程序一步一步正确地 运行 并将整个文件从源文件夹复制到目标文件夹。
那个案例让我想到,也许我正在使用 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
}
}
现在应用程序应该等到关键区域空闲,然后才尝试复制文件,这样就不会出现异常:"The process cannot access to the file 'C:..........' because it is being used by another process"。
正如我所想,没有出现异常,但应用程序仅将一个文件从源文件夹复制到目标文件夹,而不是所有文件。
当我尝试 运行 这个应用程序一步一步时,同样奇怪的事情发生了,一切正常,所有文件都被复制到目标文件夹。
为什么会这样?我该如何解决这个问题,以便将所有文件复制到普通应用程序中的目标文件夹 运行ning 而不是一步一步?
您的问题不在于 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);
}
我尝试学习如何使用线程池和互斥锁,作为一种练习,我正在尝试制作一个应用程序,将文件从计算机中的一个路径复制到计算机中的另一个路径。 为了完成这个应用程序,我使用了线程池(因此可以同时发生几个副本):
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
}
奇怪的是,当我 运行 应用程序抛出异常时:"The process cannot access to the file 'C:..........' because it is being used by another process"。 当我试图找出错误时,我 运行 应用程序一步一步正确地 运行 并将整个文件从源文件夹复制到目标文件夹。
那个案例让我想到,也许我正在使用 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
}
}
现在应用程序应该等到关键区域空闲,然后才尝试复制文件,这样就不会出现异常:"The process cannot access to the file 'C:..........' because it is being used by another process"。 正如我所想,没有出现异常,但应用程序仅将一个文件从源文件夹复制到目标文件夹,而不是所有文件。 当我尝试 运行 这个应用程序一步一步时,同样奇怪的事情发生了,一切正常,所有文件都被复制到目标文件夹。
为什么会这样?我该如何解决这个问题,以便将所有文件复制到普通应用程序中的目标文件夹 运行ning 而不是一步一步?
您的问题不在于 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);
}