2015-11-10 2 views
0

У меня есть эта функция, которая берет путь и ищет файл в нем. У меня есть два подхода: один заключается в том, чтобы основной поток выполнял задание, а другой - для того, чтобы рабочий поток выполнял эту работу. Когда основной поток выполняет задание, он возвращает все файлы, но когда рабочий поток выполняет задание, он возвращает только небольшое количество файлов только в этом пути. Он не выполняет этап рекурсии, который входит в подкаталоги. Вот код:Threading and Recursion in C#

public void GetAllFiles(string sdir) 
     { 

       foreach (string dir in Directory.GetDirectories(sdir)) 
       { 

        try 
        { 
         foreach (string file in Directory.GetFiles(dir, "*.*")) 
         { 
          string filename = Path.GetFileName(file); 

          listView1.Items.Add(filename); 

         } 
         GetAllFiles(dir); 
        } 
        catch (Exception error) 
        { 
         Console.WriteLine(error.Message); 
        } 
       } 
      } 

и вот как я называю тему:

Thread thread = new Thread(() => GetAllFiles("C:\\Users\\modz\\Desktop\\games")); 
thread.Start(); 
+1

В чем вопрос? –

+0

@ScottHunter Мне нужно знать, что вызывает это, но я думаю, что это рекурсия – mohamed

+2

Я подозреваю, что вы получаете сообщение об ошибке. Это связано с тем, что вы пытаетесь добавить элементы в объект ListView. Это неверно для любого потока, кроме потока, в который был создан ListView. Вы проверили, не были ли какие-либо ошибки записаны на консоль? –

ответ

2

Ваш код не выполняется при запуске в фоновом потоке, потому что вы пытаетесь изменить элемент управления из потока, отличного поток GUI, который не разрешен. Чтобы исправить это, вы должны скопировать результаты в какую-то коллекцию в фоновом потоке, а затем заполнить представление списка из основного потока.

Однако есть встроенная функция, которая выполняет то, что вы хотите. Поэтому самый простой и, вероятно, лучшим решением является следующее:

var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories); 
foreach (var file in files) { 
    listView1.Items.Add(file); 
} 

Если вы хотите сделать это вручную, вы можете использовать ваш метод с некоторыми изменениями, такими как:

public void GetAllFiles(string sdir, List<string> files) { 
    foreach (string dir in Directory.GetDirectories(sdir)) { 
    try { 
     foreach (string file in Directory.GetFiles(dir, "*.*")) { 
     string filename = Path.GetFileName(file); 
     files.Add(filename); 
     } 
     GetAllFiles(dir, files); 
    } catch (Exception error) { 
     Console.WriteLine(error.Message); 
    } 
    } 
} 

Вы можете позвонить это синхронно, как это:

var files = new List<string>(); 
GetAllFiles(path, files); 
foreach (var file in files) { 
    listView1.Items.Add(file); 
} 

в асинхронном случае нужно ждать фонового потока, чтобы заполнить список перед основной поток заполнит вид списка. Использование заданий и ключевые слова async и await довольно просты.

public async void Populate() { 
    const string path = ... 
    var files = new List<string>(); 
    await Task.Run(() => GetAllFiles(path, files)); 
    foreach (var file in files) { 
    listView1.Items.Add(file); 
    } 
} 
+0

Отличный ответ. Возможно, вы захотите сказать что-то о том, почему сценарий потоковой работы OP не прошел. – MickyD

+1

О, я сделал в комментарии, но это может быть не очевидно для читателей. Спасибо за напоминание! –

+0

lol, ты же знаешь, я только что понял, что ты написал комментарий наверху. (Я собирался сказать _ «Посмотрите на такой-то комментарий вверх». Мой плохой :) – MickyD

1

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

Чтобы сделать это, вы должны сделать свою жизнь простой и использовать фреймворк, который позволит вам откатить свои собственные потоки.

Я бы предложил использовать TPL, await/async или Microsoft Reactive Framework. Мой выбор таков.

Сначала определите наблюдаемую для запроса всех файлов:

public IObservable<string> GetAllFiles(string sdir) 
{ 
    return 
     from dir in Directory.GetDirectories(sdir).ToObservable(Scheduler.Default) 
     from file in Directory.GetFiles(dir, "*.*").ToObservable(Scheduler.Default) 
      .Concat(GetAllFiles(dir)) 
     select file; 
} 

Обратите внимание, что этот метод является рекурсивным и использует фоновые потоки (через Scheduler.Default), чтобы сделать работу.

Теперь вы должны потреблять его

GetAllFiles("C:\\Users\\modz\\Desktop\\games") 
    .ObserveOn(listView1) 
    .Subscribe(filename => listView1.Items.Add(filename)); 

.ObserveOn(listView1) делает все сортировочной обратно в поток пользовательского интерфейса. Параметр listView1 может быть любой формой или элементом UI.

.Subscribe(...) просто принимает все имена файлов и добавляет их в список.

Этот код должен быть очень прост в использовании.Просто NuGet Rx-WinForms или Rx-WPF, чтобы начать.