C# 在定时循环中处理多个线程,一出一入

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

我需要在一夜之间处理大量文件,并设定开始和结束时间,以避免干扰用户。我一直在调查,但现在有很多处理线程的方法,我不知道该怎么办。这些文件作为附件进入Exchange收件箱

根据这里的一些例子和一些实验,我目前的尝试是:

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