我正在开发 Windows 窗体 C# 程序,该程序每 20 分钟从共享驱动器读取 Excel 数据(我使用“计时器”)- 功能“插入”。由于性能原因,我想一次读取多个 Excel 文件。因此我使用线程。
每个线程都调用一个函数 (LoadExcelData),该函数将数据从 Excel 读取到 ArrayList。我想知道所有线程何时完成(当所有 excel 文件加载到 ArrayList 时)以便将此 ArrayList 插入到内部数据库。
我尝试使用 thread[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);
}
}
有没有更好的等待方法来解决上述问题?
最佳答案
UI 线程正在卡住,因为您使用的是 System.Windows.Forms.Timer
,它会在 UI 线程上触发计时器滴答事件;这很有用,因为您不必在勾选事件上调用
任何内容。调用 Join
会阻塞调用线程,在您的情况下,这是 UI 线程。
为了避免这种情况(并且由于您不需要调用
任何 UI 元素),您可以将 System.Windows.Forms.Timer
更改为 System.Timers.Timer
,它在与 UI 线程分开的线程中运行。如果您切换到 System.Timers.Timer
,则需要更改代码中的一些语法(例如,Tick
事件改为 Elapsed
事件,等等)。
还有 System.Thread.Timer
和 System.Web.UI.Timer
,此外,您还可以从计时器滴答事件中生成第二个线程,以避免它等待 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 中的任何内容时将其更改回来。
回答你的其他问题:
I also do not know what would happen if I have 100+ files and for this reason 100+ threads. Would that cause memory exception or some other exception?
生成 100 多个线程不会导致任何异常,除非您的代码有特定的异常(例如作为 ThreadStart
传递的空委托(delegate)),或者操作系统无法创建线程(这会导致异常)。如果操作系统无法创建线程,则会遇到更大的问题。由于 Thread
是托管对象,因此可能会占用内存(以及 ArrayList
,但 100 多个线程的内存量(在任何能够运行 .NET 框架的系统(甚至在大多数嵌入式系统上)上,即使是 1000+)也可以忽略不计,因此线程数不一定是问题。
查看您的代码,您可能需要考虑利用 System.Threading.ThreadPool
和 System.Threading.CountDownEvent
来代替生成 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 处理线程的调度,以避免在线程数量很多时出现大量资源争用。您可以使用其他方法来阻止,例如 Mutex
或 Semaphore
,但是 CountDownEvent
几乎封装了您需要使用其他方法执行的操作等待对象并加入线程池中的线程。
老实说,由于您是在多个线程中从 Excel 文件读取数据,除非每个线程将文件的全部内容读入 RAM,然后以这种方式执行操作,否则您可能不会看到性能的巨大提升。具有大量 I/O 的多线程应用程序通常不会看到巨大的性能提升,除非所述 I/O 位于注重性能的设备上或者整个文件的初始输入被读入 RAM。只是一个旁注,因为您对文件进行多线程处理。
还应该注意的是,对于您希望仅运行几秒钟左右的线程来说,使用 System.Threading.ThreadPool 是理想的选择;如果您预计线程可能需要更长的时间,那么您应该像现在一样继续生成线程。您仍然可以使用 CountDownEvent
并且不需要像您一样的线程数组(您可以只使用 (new Thread(function)).Start()
语法)。
希望能帮到你
关于c# - 执行多个线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38504297/