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

如何在c#中使用线程池和互斥?,c#,threadpool,mutex,C#,Threadpool,Mutex,我试图学习如何使用线程池和互斥,作为一种实践,我试图制作一个应用程序,将文件从计算机中的一个路径复制到计算机中的另一个路径。 为了实现此应用程序,我使用了线程池(因此可以同时生成几个副本): 到目前为止,应用程序将每个文件发送到将文件从源文件夹复制到目标文件夹的函数 CopyFile函数如下所示: static void CopyFiles(object paths) { object[] temp = paths as object[]; // The casting to objec

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

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

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
}
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,并且每个文件路径只作为参数传输一次)

为了解决这个问题,我尝试使用互斥,现在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
}
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:……’,因为它正被另一个进程使用”。 正如我所想,异常没有出现,但应用程序只将一个文件从源文件夹复制到目标文件夹,而不是所有文件。 当我试图一步一步地运行这个应用程序时,同样奇怪的事情发生了,一切正常,所有的文件都被复制到了目标文件夹


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

您的问题不是
线程池。出问题的是论点

在第一个代码段中,使用两个参数值填充对象数组,并将其传递给队列方法。这里发生的是,您总是使用相同的数组。因此,在
foreach
-循环的第一次迭代中,将两个值写入数组,并传递给它。最终,该方法将使用该对象数组在
线程池中执行。同时,在
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);  
}

尝试记录(或写入控制台)CopyFiles()中发生的情况我很确定他会在最后调用
CopyFiles
时看到使用了相同的字符串值,因为第二次到最后一次调用的取消装箱将在最后一次调用
线程池
后完成。也许这里的主题对您来说很有趣。首先,谢谢,您所说的解决了问题,但出现了另一个问题。并不是所有的文件都会一直被复制,如果我在几次运行中多次运行应用程序,程序会将整个文件复制到源文件夹中。例如:如果我运行应用程序10次,源文件夹有50个文件,那么第一次只复制了35个文件,第二次复制了所有文件,第三次只复制了10个文件,第四次复制了所有文件,在第五次,所有的文件都被复制了,等等……这实际上已经足够解决一个全新的问题了^^^你的主线程结束了这个问题(可能)。线程池线程是后台线程(不要考虑更改它)。后台线程在所有前台线程完成后终止。您可以使用任务而不是线程池,或者使用其他机制来确保所有工作线程都已完成(信号量、简单计数器等)。我已确保工作线程已完成,并且解决了问题,非常感谢!!