C# 从DragDrop事件中向线程传递数据时出现线程问题
我有一个C#应用程序,上面有一个拖放文件的按钮。我可以从我的桌面上取出6个文件,放到按钮上,让它处理这6个文件 但是,当我从DragDrop事件启动线程并将文件路径传递给从DragDrop事件中启动的新线程时,一旦线程接收到FilePath参数,文件路径就不正确 如果我通过将6个文本文件拖到按钮上来执行代码(在本例中,我必须从中删除大量代码),我将在控制台中看到以下内容: ++使用以下参数调用testthread:false、TestButton、test.txt、c:\test.txtC# 从DragDrop事件中向线程传递数据时出现线程问题,c#,multithreading,drag-and-drop,C#,Multithreading,Drag And Drop,我有一个C#应用程序,上面有一个拖放文件的按钮。我可以从我的桌面上取出6个文件,放到按钮上,让它处理这6个文件 但是,当我从DragDrop事件启动线程并将文件路径传递给从DragDrop事件中启动的新线程时,一旦线程接收到FilePath参数,文件路径就不正确 如果我通过将6个文本文件拖到按钮上来执行代码(在本例中,我必须从中删除大量代码),我将在控制台中看到以下内容: ++使用以下参数调用testthread:false、TestButton、test.txt、c:\test.txt ++使
++使用以下参数调用testthread:false、TestButton、test2.txt、c:\test2.txt
++使用以下参数调用testthread:false、TestButton、test3.txt、c:\test3.txt
++使用以下参数调用testthread:false、TestButton、test4.txt、c:\test4.txt
++使用以下参数调用testthread:false、TestButton、test5.txt、c:\test5.txt
++使用以下参数调用testthread:false、TestButton、test6.txt、c:\test6.txt
上述输出是正确的
以下输出不正确,请注意,文件路径与上述控制台输出中的文件名不匹配。
++testthread Thread-CallingfromPendingUploads==false ButtonName==TestButton CleanFileName==test.txt文件路径=c:\test2.txt
++testthread Thread-CallingfromPendingUploads==false ButtonName==TestButton CleanFileName==test1.txt文件路径=c:\test3.txt
++testthread Thread-CallingfromPendingUploads==false ButtonName==TestButton CleanFileName==test3.txt文件路径=c:\test4.txt
++testthread Thread-CallingfromPendingUploads==false ButtonName==TestButton CleanFileName==test4.txt文件路径=c:\test5.txt
++testthread Thread-CallingfromPendingUploads==false ButtonName==TestButton CleanFileName==test5.txt文件路径=c:\test5.txt
++testthread Thread-CallingfromPendingUploads==false ButtonName==TestButton CleanFileName==test6.txt文件路径=c:\test5.txt
如您所见,来自线程的文件路径与在线程启动前传递给线程的文件路径不匹配。与传递给线程的文件名相比,所有文件路径都处于关闭状态。有些文件路径是重复的,比如text5.txt 我已经为此挣扎了几个小时。有人能告诉我我做错了什么吗?
private void btnClick_DragDrop(object sender, DragEventArgs e)
{
string[] file = (string[])e.Data.GetData(DataFormats.FileDrop);
string ButtonName = "TestButton"
string[] files = new string[10];
files = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (string file in files)
{
FileInfo fileInfo = new FileInfo(file);
Console.WriteLine("++ Filename: " + fileInfo.Name + " Date of file: " + fileInfo.CreationTime + " Type of file: " + fileInfo.Extension + " Size of file: " + fileInfo.Length.ToString());
string CleanFileName = System.Web.HttpUtility.UrlEncode(fileInfo.Name.ToString());
//Start thread
try
{
Console.WriteLine("++ Calling testthread with these params: false, " + ButtonName + "," + CleanFileName + "," + file);
new Thread(() => testthread(false, ButtonName, CleanFileName, file)).Start();
Console.WriteLine("++ testthead thread started @ " + DateTime.Now);
}
catch (Exception ipwse)
{
logger.Debug(ipwse.Message + " " + ipwse.StackTrace);
}
}
}
public void testthread(bool CalledfromPendingUploads, string ButtonName, string CleanFileName, string FilePath)
{
Console.WriteLine("++ testthread Thread - CallingfromPendingUploads == " + CalledfromPendingUploads.ToString() + " ButtonName == " + ButtonName + " CleanFileName == " + CleanFileName + " FilePath = " + FilePath);
}
所有线程共享相同的
文件变量。
如果其中一个线程仅在UI线程开始下一次迭代后才开始运行,则它将使用文件
变量的下一个值
您需要在循环中声明一个单独的变量,这样每个线程都将获得自己的变量
例如:
foreach (string dontUse in files)
{
string file = dontUse;
...
}
由于文件
变量现在在循环中的作用域内,每次迭代都会得到一个单独的变量。我怀疑文件
的值可能会在这一行被覆盖:-(至少我在这里是正确的)
编辑——我同意@SLaks的答案是正确的,我理解原因。我也理解为什么我的答案不正确。与其删除它,我相信它在演示为什么锁在这种情况下不起作用方面有价值。出于这个原因,我正在做CW
在上面的代码行中进行修改可能不需要锁
我认为你不需要类似的东西:
object key = new object();
private void btnClick_DragDrop(object sender, DragEventArgs e)
{
// your code ...
//Start thread
try
{
Console.WriteLine("++ Calling testthread with these params: false, " + ButtonName + "," + CleanFileName + "," + file);
lock (key)
{
string[] fileCopy;
file.CopyTo(fileCopy);
new Thread(() => testthread(false, ButtonName, CleanFileName, fileCopy)).Start();
}
Console.WriteLine("++ testthead thread started @ " + DateTime.Now);
}
catch (Exception ipwse)
{
logger.Debug(ipwse.Message + " " + ipwse.StackTrace);
}
}
这将解决它:
string tempFile = file;
new Thread(() => testthread(false, ButtonName, CleanFileName, tempFile)).Start();
您已经陷入了lambdas和循环变量的常见陷阱,线程只是让它变得显而易见
当您创建lambda时,您使用的任何变量都将通过引用而不是您可能假设的值来关闭
这意味着当您执行以下操作时:
foreach (var outer in collection)
{
var state = 42;
Grok(() => frob(outer, state));
}
您创建的lambda在外部
上关闭,其引用在每次循环迭代中保持不变,即使其值可能会更改
// Conceptual look at the previous code
Bar outer; // outside the loop-scope
foreach (outer in collection)
{
var state = 42; // inside the loop-scope
Grok(() => frob(outer, state));
}
因此,当您在混合中引入线程时,您已经包含了一个对变量的固定引用,该变量的值正在不同线程上更改。因此,当线程速度减慢时,file
似乎跳转到最后一个值
在CleanFileName
的情况下,它是在循环内声明的,因此它在每次循环迭代时都在本地关闭。您需要遵循类似的策略来更正对文件的使用:
foreach (var outer in collection)
{
var inner = outer; // make a closure safe copy of the loop variable
var state = 42;
Grok(() => frob(inner, state));
}
您可能应该使用ThreadPool(尽管它不会有帮助)来关闭循环变量的另一种情况:您能给我一个示例,说明如何执行此操作以及代码是什么样子吗?+1,一点代码可以帮助他理解您的意思。与闭包相关的问题并不明显。-1,锁定文件
可能会出于不同的原因修复问题,但会使线程一次运行一个线程,从而产生新的问题。让我困惑的是,当我从线程输出该变量时,CleanFileName总是正确的。只有文件输出了完全错误的文件路径。@SLaks的答案是正确的,尽管乍一看他的正确性可能令人困惑。@fraXis:CleanFileName
在循环中声明,因此每个线程都有一个单独的变量。
foreach (var outer in collection)
{
var inner = outer; // make a closure safe copy of the loop variable
var state = 42;
Grok(() => frob(inner, state));
}