2016-07-21 4 views
1

Я разрабатываю программу Windows Form C#, которая считывает данные Excel с общего диска каждые 20 минут (я использую «Таймер») - функция «вставка». Я хочу читать сразу несколько файлов Excel из-за производительности. По этой причине я использую потоки.Выполнение нескольких потоков

Каждый поток вызывает функцию (LoadExcelData), которая считывает данные из Excel в ArrayList. Я хочу знать, когда все потоки закончены (когда все файлы excel были загружены в ArrayList), чтобы вставить этот ArrayList во внутреннюю базу данных.

Я пробовал с нитью [i] .Join(), но это замерзает GUI. Я также не знаю, что произойдет, если у меня будет более 100 файлов и по этой причине более 100 потоков. Может ли это вызвать исключение памяти или какое-либо другое исключение?

 //Execute every 20 minutes (Timer). Do not Execute in case previouse run is not finished 
     void inserting(List<String> excels){ 

     int numOfThreads=excels.length; 
     Thread[] threads = new Thread[numOfThreads]; 
     for (int index = 0; index < numOfThreads; index++) 
     { 
      int i = index; 
      threads[index] = new Thread(() => 
       { 
        LoadExcelData(excels[i].File_name); //function loads excel data to global array "Weather" which is used later on 
       }); 
     } 

     for (int i = 0; i < threads.Length; i++) 
     { 
      threads[i].Start(); //start thread 
     } 

     for (int i = 0; i < threads.Length; i++) 
     { 
      // threads[i].Join(); //this freezes GUI! 
     } 

     InsertToDB(object of ArrayList<ClassName>); //insert data which was read from Excels 

     isRunning=false;//Data was successefully inserted to DB 
    } 

Я хочу запускать это каждые 20 минут. Я использую таймер:

timer = new System.Windows.Forms.Timer(); 
    timer.Tick += new EventHandler(timerEventHanlder); 
    timer.Interval = 20 * 60000; // in miliseconds 
    timer.Start(); 

private void timerEventHanlder(object sender, EventArgs e) 
{ 
    List<String> excels = getExcels(); 
    if (!isRunning){ //in case previous timer even is not finished wait another 20 minutes... 
     isRunning=true; //flag to true 
     inserting(excels); 
     } 
} 

Есть ли еще лучше подождать, чтобы решить вышеуказанную проблему?

+1

Лично я почти никогда не использую нитки по причине производительности. Я использую их почти всегда, чтобы поддерживать пользовательский интерфейс. На самом деле, я думаю, что потоки _reduce_ производительность, из-за перекрестной связи связи и блокировки/синхронизации. –

+1

С самого начала я делал это для одного и того же - GUI зависал. Но тогда мне нужно было знать, когда все потоки закончены из-за таймера, и я использовал событие thread.Join(), вызвавшее снова замораживание GUI. В моем случае производительность улучшилась, потому что два (или более) файла Excel обрабатываются быстрее. – michael24B

ответ

1

Нить UI замерзает, потому что вы используете System.Windows.Forms.Timer, который запускает событие, отмеченное таймером, в потоке пользовательского интерфейса; это полезно в том, что вам не нужно Invoke что-либо в событии тика. Вызов Join блокирует вызывающий поток, а в вашем случае это поток пользовательского интерфейса.

Чтобы избежать этого (и так как вы не нуждаясь в Invoke каких-либо элементов пользовательского интерфейса), вы можете изменить ваш System.Windows.Forms.Timer к System.Timers.Timer, которая проходит в потоке отдельно от потока пользовательского интерфейса. Если вы переключитесь на System.Timers.Timer, вам нужно будет изменить некоторый синтаксис в вашем коде (например, событие Tick - это событие Elapsed и т. Д.).

Там также System.Thread.Timer и System.Web.UI.Timer, кроме того, вы также можете породить вторую нить в случае клещевого таймер, чтобы избежать его ожидания на резьбе в потоке пользовательского интерфейса, например:

private void timerEventHanlder(object sender, EventArgs e) 
{ 
    (new System.Threading.Thread(() => { 
     List<String> excels = getExcels(); 
     if (!isRunning){ //in case previous timer even is not finished wait another 20 minutes... 
      isRunning=true; //flag to true 
      inserting(excels); 
     } 
    })).Start(); 
} 

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

Ответ Ты другой вопрос, хотя:

Я также не знаю, что произойдет, если у меня есть 100 + файлы и по этой причине более 100 потоков. Может ли это вызвать исключение памяти или какое-либо другое исключение?

Нерест 100+ потоков не вызовет каких-либо исключений, если ваш код не имеет конкретного исключения (например, нулевой делегат передается в качестве ThreadStart), или если операционная система не может создать поток, который, если ОС может Не создавайте поток, у вас больше проблем. Возможно, что исчерпание памяти может произойти, поскольку Thread является управляемым объектом и, следовательно, занимает память (вместе с ArrayList, но объем памяти для более 100 потоков (даже 1000+) ничтожен в любой системе, способной работать (даже на большинстве встроенных систем), поэтому число потоков не обязательно будет проблемой.

Посмотрев на свой код, вы можете рассмотреть вместо того, чтобы создать еще 100 потоков, используя System.Threading.ThreadPool и System.Threading.CountDownEvent, пример:

CountdownEvent Countdown; 

void LoadExcelData(object data) 
{ 
    // loads excel data to global array "Weather" which is used later on 
    Countdown.Signal(); 
} 

//Execute every 20 minutes (Timer). Do not Execute in case previouse run is not finished 
void inserting(List<object> excels) 
{ 
    Countdown = new CountdownEvent(excels.Count); 
    int i = 0; 
    while (i < excels.Count) { 
     ThreadPool.QueueUserWorkItem(LoadExcelData, excels[i++].File_name); 
    } 
    Countdown.Wait(); 

    InsertToDB(WeatherList); //insert data which was read from Excels 
    isRunning = false; //Data was successefully inserted to DB 
} 

Это будет использовать пул потоков системы для выполнения ваших функций и позволяет .NET для Handl e планирование потоков, чтобы избежать массового конфликта ресурсов, если количество потоков много. Вы можете использовать другие методы для блокировки, такие как Mutex или Semaphore, но CountDownEvent в значительной степени инкапсулирует то, что вам нужно сделать с другими объектами ожидания и соединяться с потоками из пула потоков.

Если честно, поскольку вы читаете данные из файлов Excel в нескольких потоках, если только каждый поток не считывает все содержимое файла в ОЗУ, а затем выполняет эти действия таким образом, вы можете не видеть большого увеличения производительности , Многопоточные приложения, которые имеют тяжелый ввод-вывод, обычно не видят огромного увеличения производительности, если упомянутый I/O не находится в режиме производительности, или исходный ввод всего файла считывается в ОЗУ. Просто обратите внимание на то, что вы многопоточны с файлами.

Следует также отметить, что использование System.Threading.ThreadPool идеально подходит для потоков, которые вы ожидаете только в течение нескольких секунд; если вы ожидаете, что поток может занять больше времени, вы должны придерживаться нереста потоков, как сейчас. Вы все равно можете использовать CountDownEvent, и вам не нужен массив потоков, как у вас (вы можете просто использовать синтаксис (new Thread(function)).Start()).

Надеюсь, что может помочь

+0

Спасибо за ваше объяснение, это действительно полезно. Я не знал о различии между System.Timers.Timer и System.Windows.Forms.Timer. Все работает отлично! – michael24B

+1

@ michael24B, рад, что я мог бы помочь! Я обновил свой ответ с дополнительной информацией, так как заметил, что у вас возник вопрос о нерестах нескольких потоков. – txtechhelp

+0

Спасибо за ваше дополнительное объяснение.Можно ли вызвать больше функций с помощью ThreadPool.QueueUserWorkItem? Я хотел бы вызвать LoadExcelData и xy функции (ы) в том же потоке. – michael24B

1

Родительский поток собирается в цикл for, который объединяет все рабочие потоки и ждет там до тех пор, пока все потоки не будут завершены (и могут быть объединены). Если GUI работает в том же родительском потоке, выполнение не вернется к графическому интерфейсу до тех пор, пока все потоки не закончатся, что будет длительным, поскольку вы настроили таймеры. Попробуйте запустить графический интерфейс в другом потоке.

Редактирование: Также на боковой ноте я бы установил длину таймера на что-то гораздо более короткое, пока вы отлаживаете, чтобы увидеть, действительно ли он ждет вас, как вы ожидаете. Затем, как только вы его правильно поработаете, вы можете установить его на 20 минут.

+0

Любые идеи, как мне отделить поток графического интерфейса от родительского и сохранить функциональность нетронутой? Спасибо - я использую более короткий интервал (2 минуты) для целей отладки. – michael24B

+0

Без просмотра вашего кода нет. Я бы попытался вытащить все, что связано с графическим интерфейсом, в свой собственный метод, класс, независимо от того, чтобы вы могли запустить поток и отправить его для работы с графическим интерфейсом, когда ваша программа запускается. Однако я не работал с ними раньше, поэтому я не уверен, есть ли стандартные архитектуры для графических интерфейсов, за которыми вы должны следовать, особенно когда вы вводите многопоточность. –

Смежные вопросы