执行多个线程

时间:2016-07-21 12:27:16

标签: c# multithreading winforms timer threadpool

我正在开发Windows Form C#程序,每20分钟从共享驱动器读取Excel数据(我正在使用“Timer”) - 函数“插入”。由于性能的原因,我想一次读取多个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:

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

有没有更好的等待解决上述问题?

2 个答案:

答案 0 :(得分:1)

父线程将到达加入所有工作线程的for循环并等待所有线程完成(并且可以连接)。如果GUI在同一个父线程中运行,则执行不会返回到GUI,直到所有线程都已完成,这将是您设置定时器的时间很长。尝试在不同的线程中运行GUI。

编辑: 另外在旁注中,我会将您的计时器长度设置为更短的时间,同时您正在调试以确定它是否实际上正如您所期望的那样等待。然后,一旦它正常运行,您可以将其设置为20分钟。

答案 1 :(得分:1)

UI线程处于冻结状态,因为您正在使用System.Windows.Forms.Timer来触发UI线程上的计时器选中事件;这很有用,因为你不需要在{tick}事件上Invoke任何事情。调用Join会阻塞调用线程,在您的情况下,这是UI线程。

为了避免这种情况(并且由于您不需要Invoke任何UI元素),您可以将System.Windows.Forms.Timer更改为System.Timers.Timer,该Tick运行在与UI线程。如果您切换到System.Timers.Timer,则需要更改代码中的某些语法(例如Elapsed事件是System.Thread.Timer事件,等等。)

还有System.Web.UI.TimerSystem.Threading.ThreadPool,另外,你还可以在timer tick事件中产生第二个线程,以避免它等待UI线程中的线程,例如:

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();
}

启动新线程可以避免更改任何当前代码,并允许您在需要在UI中调用任何内容时将其更改回来。

回答你的另一个问题是:

  

我也不知道如果我有100多个文件会发生什么,因此有100多个帖子。这会导致内存异常或其他一些异常吗?

产生100多个线程不会导致任何异常,除非您的代码有特定的异常(如作为ThreadStart传递的空委托),或者操作系统无法创建线程,如果操作系统无法创建一个你有更大问题的线程。由于Thread是一个托管对象,因此可能会发生内存耗尽,从而占用内存(以及ArrayList,但100多个线程(甚至1000+)的内存量可以忽略不计在任何能够运行.NET框架的系统上(即使在大多数嵌入式系统上),因此线程数不一定是个问题。

查看您的代码,您可能需要考虑使用System.Threading.CountDownEvent和{{3}}来代替产生100多个线程,例如:

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处理线程的调度,以避免在线程数量很多的情况下发生大量资源争用。您可以使用其他方法来阻止,例如MutexSemaphore,但CountDownEvent几乎封装了您需要对其他等待对象执行的操作以及从线程池。

老实说,既然你是从多个线程的Excel文件中读取数据,除非每个线程将文件的全部内容读入RAM然后以这种方式执行操作,否则你可能看不到性能的大幅提升。具有大量I / O的多线程应用程序通常不会看到巨大的性能提升,除非所述I / O在具有性能意识的设备上或整个文件的初始输入被读入RAM。只是旁注,因为你正在使用文件进行多线程处理。

还应该注意的是,使用System.Threading.ThreadPool理想情况下,您希望仅运行几秒钟左右的线程;如果你预计一个线程可能需要更长的时间,你应该坚持像现在一样产生线程。你仍然可以使用CountDownEvent而不需要像你一样的线程数组(你可以只使用(new Thread(function)).Start()语法)。

希望可以提供帮助

相关问题