执行多个线程
Executing multiple threads
我正在开发 Windows Form C# 程序,它每 20 分钟从共享驱动器读取 Excel 数据(我正在使用 "Timer")- 函数 "inserting"。由于性能原因,我想一次读取多个 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);
}
}
有没有更好的办法解决以上问题?
父线程将到达连接所有工作线程的 for 循环并在那里等待,直到所有线程都完成(并且可以连接)。如果 GUI 在同一个父线程中是 运行,则在所有线程完成之前,执行不会 return 到 GUI,这将是一个很长的时间,因为您已经设置了计时器。在不同的线程中尝试 运行 GUI。
编辑:
另外在旁注中,我会在调试时将您的计时器长度设置得更短,以查看它是否真的像您期望的那样等待。然后,一旦您使其正常运行,您就可以将其设置回 20 分钟。
UI 线程正在冻结,因为您正在使用 System.Windows.Forms.Timer
来触发 UI 线程上的计时器滴答事件;这很有用,因为您不必 Invoke
在 tick 事件上做任何事情。调用 Join
会阻塞调用线程,在您的情况下,这是 UI 线程。
为避免这种情况(并且由于您不需要 Invoke
任何 UI 元素),您可以将 System.Windows.Forms.Timer
更改为 System.Timers.Timer
, which runs in a thread separate from the UI thread. If you switch to a System.Timers.Timer
, you'll need to change some of the syntax in your code (e.g. the Tick
event is the Elapsed
事件,等)。
还有 System.Thread.Timer
and the 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
传递的空委托),或者如果 OS 无法创建thread,如果 OS 不能创建一个线程,你就会遇到更大的问题。可能会发生内存耗尽,因为 Thread
是一个托管对象,因此会占用内存(连同 ArrayList
,但是 100+ 线程(甚至 1000+)的内存量可以忽略不计在任何能够 运行 运行 .NET 框架的系统上(甚至在大多数嵌入式系统上),因此线程数量不一定是问题。
查看您的代码,您可能需要考虑利用 System.Threading.ThreadPool
and a 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()
语法)。
希望能帮到你
我正在开发 Windows Form C# 程序,它每 20 分钟从共享驱动器读取 Excel 数据(我正在使用 "Timer")- 函数 "inserting"。由于性能原因,我想一次读取多个 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);
}
}
有没有更好的办法解决以上问题?
父线程将到达连接所有工作线程的 for 循环并在那里等待,直到所有线程都完成(并且可以连接)。如果 GUI 在同一个父线程中是 运行,则在所有线程完成之前,执行不会 return 到 GUI,这将是一个很长的时间,因为您已经设置了计时器。在不同的线程中尝试 运行 GUI。
编辑: 另外在旁注中,我会在调试时将您的计时器长度设置得更短,以查看它是否真的像您期望的那样等待。然后,一旦您使其正常运行,您就可以将其设置回 20 分钟。
UI 线程正在冻结,因为您正在使用 System.Windows.Forms.Timer
来触发 UI 线程上的计时器滴答事件;这很有用,因为您不必 Invoke
在 tick 事件上做任何事情。调用 Join
会阻塞调用线程,在您的情况下,这是 UI 线程。
为避免这种情况(并且由于您不需要 Invoke
任何 UI 元素),您可以将 System.Windows.Forms.Timer
更改为 System.Timers.Timer
, which runs in a thread separate from the UI thread. If you switch to a System.Timers.Timer
, you'll need to change some of the syntax in your code (e.g. the Tick
event is the Elapsed
事件,等)。
还有 System.Thread.Timer
and the 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
传递的空委托),或者如果 OS 无法创建thread,如果 OS 不能创建一个线程,你就会遇到更大的问题。可能会发生内存耗尽,因为 Thread
是一个托管对象,因此会占用内存(连同 ArrayList
,但是 100+ 线程(甚至 1000+)的内存量可以忽略不计在任何能够 运行 运行 .NET 框架的系统上(甚至在大多数嵌入式系统上),因此线程数量不一定是问题。
查看您的代码,您可能需要考虑利用 System.Threading.ThreadPool
and a 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()
语法)。
希望能帮到你