混合螺纹定时器和PLINQ

时间:2014-07-03 16:26:11

标签: c# multithreading timer plinq

在线程计时器回调中调用PLINQ的ForAll扩展时遇到问题。这将无限期地创建线程。代码示例是实际问题的简单向下剥离版本。

class Program
{
    static List<int> x = new List<int>();

    static void Main(string[] args)
    {                   
        x = Enumerable.Range(0, 9).ToList();
        System.Threading.Timer[] timers = new System.Threading.Timer[10];
        for (int i = 0; i < 10; i++)
            timers[i] = new System.Threading.Timer(ElapsedCallback, null, 1000, 1000);
        Console.ReadLine();
    }

    static void ElapsedCallback(object state)
    {
        int id = Thread.CurrentThread.ManagedThreadId;
        x.AsParallel().ForAll(y => Console.WriteLine(y + " - " + Thread.CurrentThread.ManagedThreadId + " - " + id));
    }
}

如果在taskmanager中监视,可以看到线程计数将提升,直到进程挂起。如果我限制ThreadPool Size,那么进程将创建线程,直到该大小然后也被卡住。

如果在其他代码中看到该模式也。例如,Firebird ADO.Net Provider中的ConnectionPool也以这种方式执行清理未使用的连接。如果我在这里做一些愚蠢的话,我不是唯一一个;)任何见解?

编辑:吉姆要求提供一些背景信息......

此模式用于ReadOnly Transactions的事务池。该应用程序可能在十几个不同的数据库中打开了多个只读事务。每个数据库都有自己的事务池和自己的Timer,它定期提交和处理池中该数据库的旧事务。然后通过PLINQ的ForAll并行化每个事务提交。

1 个答案:

答案 0 :(得分:2)

您正在创建10个计时器,每个计时器每秒打一次。对于那些计时器,所有输出所有数据的时间都会超过一秒钟。那么你得到每个计时器的 next 标记,并且将创建更多线程,在下一个计时器之前不会完成,并且。 。 。是的,那永远不会完成。

即使您只使用一个计时器进行此操作,回调也可能在下一个计时器之前完成,一秒钟之后。如果您使用单个计时器,则可以通过以下几种方法之一解决问题:

  1. 在您输入回叫时禁用计时器,并在退出时重新启用它。这将阻止(在大多数情况下)在您处理回调时发生滴答声。但是,有些情况下仍可能出现多个滴答,最常见的情况是系统负载过重。
  2. 创建锁定对象并使用Monitor.TryEnter尝试在输入回调时获取锁定。如果您无法获得锁定,请退出。当然,如果您确实获得了锁定,那么您需要在退出回调之前致电Monitor.Exit
  3. 让你的计时器一次性使它只触发一次。当回调完成其工作时,回调再次初始化计时器,再次作为一次性。这很好用,虽然你的回调是在最后一次回调结束后一秒钟执行的,而不是每秒执行一次。
  4. 如果没有关于您真正想要做的事情的更多信息,则无法提供更具体的建议。