C# 在定时循环中处理多个线程,一出一入
我需要在一夜之间处理大量文件,并设定开始和结束时间,以避免干扰用户。我一直在调查,但现在有很多处理线程的方法,我不知道该怎么办。这些文件作为附件进入Exchange收件箱 根据这里的一些例子和一些实验,我目前的尝试是:C# 在定时循环中处理多个线程,一出一入,c#,multithreading,timer,batch-processing,C#,Multithreading,Timer,Batch Processing,我需要在一夜之间处理大量文件,并设定开始和结束时间,以避免干扰用户。我一直在调查,但现在有很多处理线程的方法,我不知道该怎么办。这些文件作为附件进入Exchange收件箱 根据这里的一些例子和一些实验,我目前的尝试是: while (DateTime.Now < dtEndTime.Value) { var finished = new CountdownEvent(1); for (int i = 0; i < numThread
while (DateTime.Now < dtEndTime.Value)
{
var finished = new CountdownEvent(1);
for (int i = 0; i < numThreads; i++)
{
object state = offset;
finished.AddCount();
ThreadPool.QueueUserWorkItem(delegate
{
try
{
StartProcessing(state);
}
finally
{
finished.Signal();
}
});
offset += numberOfFilesPerPoll;
}
finished.Signal();
finished.Wait();
}
while(DateTime.Now
为了方便起见,它目前正在winforms应用程序中运行,但核心处理在dll中,因此我可以从windows服务、在调度程序下运行的控制台生成所需的类,但这是最简单的。我确实有一个Windows服务,它设置了一个计时器对象,可以在配置文件中设置的时间启动处理
所以我的问题是——在上面的代码中,我初始化了一堆线程(目前为10个),然后等待它们全部处理。我的理想是一个静态的线程数,当一个线程完成时,我触发另一个线程,然后当我到达结束时间时,我只是等待所有线程完成。
这是因为我正在处理的文件大小不一——有些可能需要几秒钟的时间来处理,有些可能需要几个小时,所以我不希望整个应用程序在一个线程完成时等待,如果我可以让它在后台运行的话。
(编辑)目前,每个线程实例化一个类并传递一个偏移量。然后,该类从收件箱中获取下一封x封电子邮件,从偏移量开始(使用Exchange Web服务分页功能)。处理每个文件时,都会将其移动到单独的文件夹中。从目前的一些回复来看,我想知道我是否应该在外部循环中获取电子邮件,并根据需要生成线程。
为了解决这个问题,我目前有一大堆积压的电子邮件,我正试图处理它们。一旦清除了积压工作,夜间运行的负载可能会显著降低
平均每天晚上大约要处理1000个文件
更新
我重写了大量代码,以便使用Parallel.Foreach,我遇到了线程安全问题。调用代码现在如下所示:
public bool StartProcessing()
{
FindItemsResults<Item> emails = GetEmails();
var source = new CancellationTokenSource(TimeSpan.FromHours(10));
// Process files in parallel, with a maximum thread count.
var opts = new ParallelOptions { MaxDegreeOfParallelism = 8, CancellationToken = source.Token };
try
{
Parallel.ForEach(emails, opts, processAttachment);
}
catch (OperationCanceledException)
{
Console.WriteLine("Loop was cancelled.");
}
catch (Exception err)
{
WriteToLogFile(err.Message + "\r\n");
WriteToLogFile(err.StackTrace + "r\n");
}
return true;
}
IEnumerable<string> EnumerateFiles()
{
foreach (var file in Directory.EnumerateFiles( "*.txt" ))
if (DateTime.Now < _endTime)
yield return file;
else
yield break;
}
var query =
from file in filesToProcess.ToObservable()
where DateTime.Now < stopTime
from result in Observable.Start(() => StartProcessing(file))
select new { file, result };
var subscription =
query.Subscribe(x =>
{
/* handle result */
});
public bool启动处理()
{
FindItemsResults电子邮件=GetEmails();
var source=新的CancellationTokenSource(TimeSpan.FromHours(10));
//以最大线程数并行处理文件。
var opts=new ParallelOptions{maxdegreeofpparallelism=8,CancellationToken=source.Token};
尝试
{
Parallel.ForEach(电子邮件、选择、处理附件);
}
捕获(操作取消异常)
{
WriteLine(“循环被取消”);
}
捕获(异常错误)
{
WriteToLogFile(err.Message+“\r\n”);
WriteToLogFile(err.StackTrace+“r\n”);
}
返回true;
}
到目前为止还不错(请原谅临时错误处理)。我现在有一个新的问题,即“Item”对象(电子邮件)的属性不是线程安全的。例如,当我开始处理一封电子邮件时,我将它移动到一个“正在处理”文件夹中,这样另一个进程就无法抓取它——但结果表明,几个线程可能一次处理同一封电子邮件。我如何保证这不会发生?我知道我需要添加一个锁,我可以在ForEach中添加它吗,还是应该在processAttachments方法中添加它?使用TPL:
Parallel.ForEach( EnumerateFiles(),
new ParallelOptions { MaxDegreeOfParallelism = 10 },
file => ProcessFile( file ) );
当到达结束时间时,使枚举文件
停止枚举,如下所示:
public bool StartProcessing()
{
FindItemsResults<Item> emails = GetEmails();
var source = new CancellationTokenSource(TimeSpan.FromHours(10));
// Process files in parallel, with a maximum thread count.
var opts = new ParallelOptions { MaxDegreeOfParallelism = 8, CancellationToken = source.Token };
try
{
Parallel.ForEach(emails, opts, processAttachment);
}
catch (OperationCanceledException)
{
Console.WriteLine("Loop was cancelled.");
}
catch (Exception err)
{
WriteToLogFile(err.Message + "\r\n");
WriteToLogFile(err.StackTrace + "r\n");
}
return true;
}
IEnumerable<string> EnumerateFiles()
{
foreach (var file in Directory.EnumerateFiles( "*.txt" ))
if (DateTime.Now < _endTime)
yield return file;
else
yield break;
}
var query =
from file in filesToProcess.ToObservable()
where DateTime.Now < stopTime
from result in Observable.Start(() => StartProcessing(file))
select new { file, result };
var subscription =
query.Subscribe(x =>
{
/* handle result */
});
IEnumerable枚举文件()
{
foreach(Directory.EnumerateFiles(“*.txt”)中的var文件)
如果(DateTime.Now<\u endTime)
生成返回文件;
其他的
屈服断裂;
}
使用第三方物流:
Parallel.ForEach( EnumerateFiles(),
new ParallelOptions { MaxDegreeOfParallelism = 10 },
file => ProcessFile( file ) );
当到达结束时间时,使枚举文件
停止枚举,如下所示:
public bool StartProcessing()
{
FindItemsResults<Item> emails = GetEmails();
var source = new CancellationTokenSource(TimeSpan.FromHours(10));
// Process files in parallel, with a maximum thread count.
var opts = new ParallelOptions { MaxDegreeOfParallelism = 8, CancellationToken = source.Token };
try
{
Parallel.ForEach(emails, opts, processAttachment);
}
catch (OperationCanceledException)
{
Console.WriteLine("Loop was cancelled.");
}
catch (Exception err)
{
WriteToLogFile(err.Message + "\r\n");
WriteToLogFile(err.StackTrace + "r\n");
}
return true;
}
IEnumerable<string> EnumerateFiles()
{
foreach (var file in Directory.EnumerateFiles( "*.txt" ))
if (DateTime.Now < _endTime)
yield return file;
else
yield break;
}
var query =
from file in filesToProcess.ToObservable()
where DateTime.Now < stopTime
from result in Observable.Start(() => StartProcessing(file))
select new { file, result };
var subscription =
query.Subscribe(x =>
{
/* handle result */
});
IEnumerable枚举文件()
{
foreach(Directory.EnumerateFiles(“*.txt”)中的var文件)
如果(DateTime.Now<\u endTime)
生成返回文件;
其他的
屈服断裂;
}
您可以结合使用Parallel.ForEach()
和取消令牌源,该令牌源将在设置的时间后取消操作:
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
static class Program
{
static Random rng = new Random();
static void Main()
{
// Simulate having a list of files.
var fileList = Enumerable.Range(1, 100000).Select(i => i.ToString());
// For demo purposes, cancel after a few seconds.
var source = new CancellationTokenSource(TimeSpan.FromSeconds(10));
// Process files in parallel, with a maximum thread count.
var opts = new ParallelOptions {MaxDegreeOfParallelism = 8, CancellationToken = source .Token};
try
{
Parallel.ForEach(fileList, opts, processFile);
}
catch (OperationCanceledException)
{
Console.WriteLine("Loop was cancelled.");
}
}
static void processFile(string file)
{
Console.WriteLine("Processing file: " + file);
// Simulate taking a varying amount of time per file.
int delay;
lock (rng)
{
delay = rng.Next(200, 2000);
}
Thread.Sleep(delay);
Console.WriteLine("Processed file: " + file);
}
}
}
作为使用取消令牌的替代方法,您可以编写一个返回IEnumerable
的方法,该方法返回文件名列表,并在时间结束时停止返回它们,例如:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
static class Program
{
static Random rng = new Random();
static void Main()
{
// Process files in parallel, with a maximum thread count.
var opts = new ParallelOptions {MaxDegreeOfParallelism = 8};
Parallel.ForEach(fileList(), opts, processFile);
}
static IEnumerable<string> fileList()
{
// Simulate having a list of files.
var fileList = Enumerable.Range(1, 100000).Select(x => x.ToString()).ToArray();
// Simulate finishing after a few seconds.
DateTime endTime = DateTime.Now + TimeSpan.FromSeconds(10);
int i = 0;
while (DateTime.Now <= endTime)
yield return fileList[i++];
}
static void processFile(string file)
{
Console.WriteLine("Processing file: " + file);
// Simulate taking a varying amount of time per file.
int delay;
lock (rng)
{
delay = rng.Next(200, 2000);
}
Thread.Sleep(delay);
Console.WriteLine("Processed file: " + file);
}
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统线程;
使用System.Threading.Tasks;
名称空间演示
{
静态类程序
{
静态随机rng=新随机();
静态void Main()
{
//以最大线程数并行处理文件。
var opts=newparalleloptions{maxdegreeofpparallelism=8};
Parallel.ForEach(fileList(),opts,processFile);
}
静态IEnumerable文件列表()
{
//模拟拥有一个文件列表。
var fileList=Enumerable.Range(11000)。选择(x=>x.ToString()).ToArray();
/