在线程计时器回调中调用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并行化每个事务提交。
答案 0 :(得分:2)
您正在创建10个计时器,每个计时器每秒打一次。对于那些计时器,所有输出所有数据的时间都会超过一秒钟。那么你得到每个计时器的 next 标记,并且将创建更多线程,在下一个计时器之前不会完成,并且。 。 。是的,那永远不会完成。
即使您只使用一个计时器进行此操作,回调也可能在下一个计时器之前完成,一秒钟之后。如果您使用单个计时器,则可以通过以下几种方法之一解决问题:
Monitor.TryEnter
尝试在输入回调时获取锁定。如果您无法获得锁定,请退出。当然,如果您确实获得了锁定,那么您需要在退出回调之前致电Monitor.Exit
。如果没有关于您真正想要做的事情的更多信息,则无法提供更具体的建议。