混合线程定时器和PLINQ
本文关键字:PLINQ 定时器 线程 混合 | 更新日期: 2023-09-27 18:11:24
我在线程计时器回调中调用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));
}
}
如果在任务管理器中监视,可以看到线程数将上升,直到进程挂起。如果我限制线程池的大小,进程将创建线程直到这个大小,然后也卡住了。
如果在其他代码中也看到这种模式。例如Firebird ADO中的ConnectionPool。Net Provider这样做也是为了清理未使用的连接。如果我在这里做一些愚蠢的事情,我不是唯一一个;)见解吗?
编辑:Jim问了一些背景,所以…此模式用于只读事务的事务池。应用程序可能在十几个不同的数据库中打开了多个只读事务。每个数据库都有自己的事务池(Pool of Transaction)和自己的Timer,后者定期提交和处置池中该数据库的旧事务。然后通过PLINQ的ForAll并行处理提交的每个事务。
您将创建10个计时器,每个计时器将每秒滴答一次。这些计时器需要超过一秒的时间来输出所有的数据。因此,您将为每个计时器获得下一个标记,并且将创建更多在下一个计时器之前不会完成的线程,并且…是啊,那是永远不会完成的。
即使只使用一个计时器,回调也有可能在下一个计时器之前完成,也就是一秒钟之后。如果您使用单个计时器,您可以使用以下几种方法之一来解决这个问题:
- 回呼时禁用定时器,退出时重新启用定时器。这将防止(在大多数情况下)在处理回调时发生滴答声。然而,在某些情况下,多个节拍仍然可能发生,最常见的情况是系统负载过重。
- 创建一个锁对象,并在进入回调时使用
Monitor.TryEnter
尝试获取锁。如果无法获得锁,则退出。当然,如果你确实获得了锁,那么你会想在退出回调之前调用Monitor.Exit
。 - 将计时器设置为一次性,以便它只触发一次。当回调完成它的工作时,回调将重新初始化计时器,同样是一次性的。这工作得很好,尽管你的回调是在最后一个回调结束后一秒钟执行的,而不是每秒一次。
如果没有更多关于你真正想做什么的信息,就不可能给出更具体的建议。