C# 使用一组有限的线程定期启动类似的任务

C# 使用一组有限的线程定期启动类似的任务,c#,multithreading,visual-studio-2010,timer,threadpool,C#,Multithreading,Visual Studio 2010,Timer,Threadpool,我以前也问过类似的问题,但经过深思熟虑,以及那些回答我的人的实施,我发现我的方法可能不正确 当我执行上一个问题的解决方案时,出现了以下测试结果: 当我“模拟”线程池中多个线程上并发运行的多个任务时(例如,通过使线程在1到20秒的随机时间睡眠),那么模型似乎工作正常。我将系统设置为每1秒轮询一次,以查看是否可以生成另一个线程,并且一切都似乎正常。长时间运行(休眠)的线程将在稍后完成,并且线程将在整个位置启动和终止。如果我碰巧用完了线程(我将其设置为生成的线程不超过10个),它将坐在那里等待一个线程

我以前也问过类似的问题,但经过深思熟虑,以及那些回答我的人的实施,我发现我的方法可能不正确

当我执行上一个问题的解决方案时,出现了以下测试结果:

  • 当我“模拟”线程池中多个线程上并发运行的多个任务时(例如,通过使线程在1到20秒的随机时间睡眠),那么模型似乎工作正常。我将系统设置为每1秒轮询一次,以查看是否可以生成另一个线程,并且一切都似乎正常。长时间运行(休眠)的线程将在稍后完成,并且线程将在整个位置启动和终止。如果我碰巧用完了线程(我将其设置为生成的线程不超过10个),它将坐在那里等待一个线程可用
  • 然而,当我让系统在每个线程中进行实际处理(这将花费3秒以上的时间),包括读取数据、生成XML保存数据、发送电子邮件等,系统将生成1、2或3个线程,进行处理,然后关闭线程(3…2…1…),然后说0个线程正在运行(我在每个地方都添加了console.writelines来记录这个过程)。然后它将挂起0个线程,不再挑选任何工作
  • 因此,我决定再次陈述我的问题,希望有人能找到解决方案。到目前为止,我已经尝试了各种解决方案:

  • 线程池:总是有人提到你不应该过度工作线程池,作业必须是“快速”的,但是“快速”的定义是什么?我如何知道线程池有多大/多忙
  • 线程:人们总是说线程是昂贵的,你必须在开始和结束时处理它们,但我如何限制它们,我尝试过信号量、“锁定”对象、公共变量,但都没有用
  • 因此,以下是我想要实现的目标:

  • 我有同样的工作,需要定期运行,例如像gmail一样,每5秒钟为你检查一次服务器上的新电子邮件
  • 如果有工作要做(即,您有新的电子邮件要发送到收件箱),则生成一个异步线程并使其开始工作。此工作通常需要比(1)中规定的时间间隔更长的时间,因此是异步线程,如果一个时间间隔过去,系统再次检查是否有新的工作,并看到您有更多的工作,它将生成另一个线程并使其开始工作
  • 在我的例子中,所有作业都是同一种作业(检查新邮件),并且彼此完全独立,它们不会相互影响。如果其中一个失败,其余的可以继续工作,而不会产生任何问题
  • 我需要有一个并发线程数和最大线程数的限制。如果我选择“10”,那么系统应该像(1)中那样开始检查作业,并像(1)中那样继续生成线程,直到达到10个线程。在一段时间间隔内生成新线程的所有新尝试都应该失败(不执行任何操作)直到一个线程再次被释放。我想这里的选择是:(a)当它被释放时,已经有一些工作排队等待分配给新的开放线程,或者(b)在下一个间隔检查是否有新的工作,并将其分配给新的开放线程
  • 如果没有工作,那么系统通常应该静坐等待,没有线程,本质上应该运行的只有某种计时器
  • 我目前使用上一个问题中的示例执行以下操作:

  • 我启动一个计时器,每1秒一次
  • 在每个勾号上输入“ThreadPool.QueueUserWorkItem(新建WaitCallback(DoWork)”
  • 在DoWork中,我实例化了一个类,并调用了执行某些工作的各种方法
  • …但这导致了我前面提到的,只有3个线程会消失,然后什么都没有

    我正在考虑做以下事情:

  • 将线程池设置为10个线程
  • 启动一个计时器,在每个线程池中勾选“ThreadPool.QueueUserWorkItem”,然后继续这样做,希望线程池能够处理其他所有事情。这不是线程池应该做的吗
  • 任何帮助都将非常好!(很抱歉有这么多解释!)

    试着看看这个类。你可以用它来设置一个限制,限制有多少线程可以同时访问特定的资源(当我说resource时,它可以是任何东西)

    好的,编辑以了解详细信息:

    在管理线程的类中,您创建:

    Semaphore concurrentThreadsEnforcer = new Semaphore(value1, value2);
    
    然后,启动的每个线程将调用:

    concurrentThreadsEnforcer.WaitOne();
    
    这将从信号量中获取一个插槽并将其提供给新线程,或者阻止新线程,直到有一个插槽可用为止

    每当你的新线程完成它的工作,他(我喜欢个性化)必须打电话,原因很明显:

    concurrentThreadsEnforcer.Release().
    
    现在,关于构造函数,第二个参数相当简单:说明在任何给定时间有多少并发线程可以访问资源

    第一个参数有点棘手。第二个参数和第一个参数之间的差异将说明为调用线程保留了多少个信号量插槽。也就是说,所有新生成的线程都将访问第一个参数所述的插槽数量,其余线程将访问到second参数的值将保留给创建信号量的原始线程(调用线程)

    在您的情况下,对于最多10个线程,您将使用:

    ... = new Semaphore(10, 10);
    
    既然我已经发布了一个故事,让我来嘲弄一下更多的细节

    我将在新线程中这样做:

    bool aquired = false;
    try
    {
        aquired = concurrentThreadsEnforcer.WaitOne();
    
        // Do some work here
    } // Optional catch statements
    finally
    {
        if (aquired)
            concurrentThreadsEnforcer.Release();;
    }
    

    我会结合使用
    BlockingCollection
    Parallel.ForEach

    大概是这样的:

    private BlockingCollection<Job> jobs = new BlockingCollection<Job>();
    private Task jobprocessor;
    
    public void StartWork() {
        timer.Start();
        jobprocessor = Task.Factory.StartNew(RunJobs);
    }
    
    public void EndWork() {
        timer.Stop();
        jobs.CompleteAdding();
        jobprocessor.Wait();
    }
    
    public void TimerTick() {
       var job = new Job();
       if (job.NeedsMoreWork())
           jobs.Add(job);
    }
    
    public void RunJobs() {
        var options = new ParallelOptions { MaxDegreeOfParallelism = 10 };
        Parallel.ForEach(jobs.GetConsumingPartitioner(), options,
                         job => job.DoSomething());
    }
    
    private BlockingCollection作业=新建BlockingCollection();
    专用任务处理器;
    公众的