Проблема с передачей данных потоку из события DragDrop - PullRequest
4 голосов
/ 22 июля 2011

У меня есть приложение 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 - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test.txt FilePath = c: \ test2.txt
++ Поток testthread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test1.txt FilePath = c: \ test3.txt
++ Поток testthread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test3.txt FilePath = c: \ test4.txt
++ Поток testthread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test4.txt FilePath = c: \ test5.txt
++ Поток testthread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test5.txt FilePath = c: \ test5.txt
++ Поток testthread - CallingfromPendingUploads == false ButtonName == TestButton CleanFileName == test6.txt FilePath = c: \ test5.txt

Как видите, FilePath из потока не совпадает с FilePath, который передается в поток перед его запуском. Все FilePaths отключены по сравнению с именем файла, которое передается в поток. И некоторые из FilePaths являются дубликатами, такими как 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 ]

4 голосов
/ 22 июля 2011

Все ваши темы имеют общую переменную file.
Если один из потоков запускается только после того, как поток пользовательского интерфейса запустил следующую итерацию, он будет использовать следующее значение переменной file.

Вам необходимо объявить отдельную переменную внутри цикла, чтобы каждый поток получал свою собственную переменную.

Например:

foreach (string dontUse in files)
{
    string file = dontUse;
    ...
}

Поскольку переменная file теперь находится в пределах цикла, каждая итерация получает отдельную переменную.

2 голосов
/ 22 июля 2011

это все исправит:

string tempFile = file;
new Thread(() => testthread(false, ButtonName, CleanFileName, tempFile)).Start();
1 голос
/ 22 июля 2011

Вы попали в общую ловушку лямбда-переменных и переменных цикла, потоки только что сделали это очевидным.

Когда вы создаете лямбду, все переменные, которые вы используете, будут закрываться по по ссылке , а не по значению, как вы могли предположить.

Что это означает, что когда вы делаете:

foreach (var outer in collection)
{
    var state = 42;
    Grok(() => frob(outer, state));
}

Лямбда, которую вы создали, закрыта по 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));
}
0 голосов
/ 22 июля 2011

Я подозреваю, что значение file может быть перезаписано в этой строке: - (, по крайней мере, я здесь был прав )

new Thread(() => testthread(false, ButtonName, CleanFileName, file)).Start();

Редактировать - Я согласен, что ответ @SLaks правильный, и я понимаю, почему.Я также понимаю, почему мой ответ неверен.Вместо того, чтобы удалять его, я полагаю, что это имеет смысл продемонстрировать, почему блокировка не будет работать в этом случае.И по этой причине я делаю это CW.

Возможно, блокировка НЕ необходима с изменением в строке кода выше.

I НЕ думаю, что вам нужно что-то похожее на это:

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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...