从DragDrop事件中将数据传递给线程的线程问题

时间:2011-07-21 22:43:37

标签: 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

以上输出正确


以下输出不正确,请注意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);
}

4 个答案:

答案 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);
    }
}