我有一个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
以上输出正确
以下输出不正确,请注意FilePath与上述控制台输出中的CleanFileName不匹配。
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test.txt FilePath = c:\ test2.txt
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test1.txt FilePath = c:\ test3.txt
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test3.txt FilePath = c:\ test4.txt
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test4.txt FilePath = c:\ test5.txt
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test5.txt FilePath = c:\ test5.txt
++ testthread Thread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test6.txt FilePath = c:\ test5.txt
如您所见,来自线程的FilePath与在启动之前传递给Thread的FilePath不匹配。与传递给Thread的文件名相比,所有FilePath都处于关闭状态。并且一些FilePath是重复的,例如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);
}
答案 0 :(得分:4)
您的所有主题都共享相同的file
变量
如果其中一个线程仅在UI线程开始下一次迭代后才开始运行,它将使用file
变量的下一个值。
你需要在循环中声明一个单独的变量,这样每个线程都将获得自己的变量。
例如:
foreach (string dontUse in files)
{
string file = dontUse;
...
}
由于file
变量现在在循环中作用域,因此每次迭代都会获得一个单独的变量。
答案 1 :(得分:2)
这将解决它:
string tempFile = file;
new Thread(() => testthread(false, ButtonName, CleanFileName, tempFile)).Start();
答案 2 :(得分:1)
你陷入了lambdas和循环变量的常见陷阱,线程只是显而易见。
创建lambda时,您使用的任何变量都将通过按引用关闭,而不是按您所假设的值关闭。
这意味着当你这样做时:
foreach (var outer in collection)
{
var state = 42;
Grok(() => frob(outer, state));
}
您创建的lambda关闭outer
,其引用在每次循环迭代时保持不变,即使它的值可能会改变!
// 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
的情况下,它在循环中声明 ,因此在每次循环迭代时在本地上关闭。您需要遵循类似的策略来更正file
的使用情况:
foreach (var outer in collection)
{
var inner = outer; // make a closure safe copy of the loop variable
var state = 42;
Grok(() => frob(inner, state));
}
答案 3 :(得分:-1)
我怀疑file
的价值可能会在此行中被覆盖: - (至少我在这里是正确的)
new Thread(() => testthread(false, ButtonName, CleanFileName, file)).Start();
编辑 - 我同意@SLaks的答案是正确的,我理解为什么。我也理解为什么我的答案不正确。我相信它不会删除它,而是有助于证明锁在这种情况下不会工作的原因。因此,我正在制作CW。
在上面的代码行中修改可能需要 NOT 。
我不要认为你需要接近这个的东西:
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);
}
}