我用了这个问题描述的调用:Synchronization accross threads/atomic checks?需要帮助跟踪螺纹冻结
我需要创建一个方法调用任何线程可以调用,这将在主要执行的线程上执行具体给定的执行点。
我结束了使用该实施Invoker
类: 我知道这是可能不是最有效的锁定方面,但它以类似的足够方式理论上的工作比Thread.MemoryBarrier()
,如一个SLaks建议。
编辑:有了MRAB的建议。
public class Invoker
{
private Queue<Action> Actions { get; set; }
public Invoker()
{
this.Actions = new Queue<Action>();
}
public void Execute()
{
Console.WriteLine("Executing {0} actions on thread {1}", this.Actions.Count, Thread.CurrentThread.ManagedThreadId);
while (this.Actions.Count > 0)
{
Action action;
lock (this.Actions)
{
action = this.Actions.Dequeue();
}
action();
}
Console.WriteLine("Executed, {0} actions left", this.Actions.Count);
}
public void Invoke(Action action, bool block = true)
{
if (block)
{
Console.WriteLine("Invoking");
SemaphoreSlim semaphore = new SemaphoreSlim(0, 1);
lock (this.Actions)
{
this.Actions.Enqueue(delegate
{
try
{
action();
Console.WriteLine("Actioned");
}
catch
{
Console.WriteLine("Exception thrown by action");
throw;
}
finally
{
semaphore.Release();
Console.WriteLine("Released");
}
});
}
Console.WriteLine("Enqueued");
Console.WriteLine("Waiting on thread {0}", Thread.CurrentThread.ManagedThreadId);
semaphore.Wait();
Console.WriteLine("Waited");
semaphore.Dispose();
}
else
{
this.Actions.Enqueue(action);
}
}
}
的许多Console.WriteLine
在那里帮助我跟踪我的冻结,这恰好不管这个记录是否存在(即,他们是不负责的冷冻和可丢弃的罪魁祸首)。
凝固发生在一个场景:
- 执行线程在一个循环中(呼叫
Invoker.Execute
)运行。 - 在其他两个线程上,相对同时调用2个方法(调用
Invoker.Invoke
)。 - 第一种方法可以正常工作并被正确调用,但第二种方法在“等待”之后冻结,也就是在
semaphore.Wait()
之后冻结。
输出示例:
Executing 0 actions on thread 1
Executed, 0 actions left
Executing 0 actions on thread 1
Executed, 0 actions left
Invoking
Enqueued
Waiting on thread 7
Executing 1 actions on thread 1
Actioned
Released
Executed, 0 actions left
Waited
Invoking
Enqueued
Waiting on thread 8
我嫌疑犯是发生的是,执行线程不知何故块,因此不执行所述第二排队的动作,并且不释放信号量(semaphore.Release()
) ,因此不允许执行继续。
但是,这是非常奇怪的(在我看来),因为执行是在另一个线程比信号量阻塞,所以它不应该阻止,对不对?
我试过构建一个测试用例,它将上下文环境中的问题复制出来,但我无法获得它的重现。我在这里发布它作为前面解释的3个步骤的说明。
static class Earth
{
public const bool IsRound = true;
}
class Program
{
static Invoker Invoker = new Invoker();
static int i;
static void TestInvokingThread()
{
Invoker.Invoke(delegate { Thread.Sleep(300); }); // Simulate some work
}
static void TestExecutingThread()
{
while (Earth.IsRound)
{
Thread.Sleep(100); // Simulate some work
Invoker.Execute();
Thread.Sleep(100); // Simulate some work
}
}
static void Main(string[] args)
{
new Thread(TestExecutingThread).Start();
Random random = new Random();
Thread.Sleep(random.Next(3000)); // Enter at a random point
new Thread(TestInvokingThread).Start();
new Thread(TestInvokingThread).Start();
}
}
输出(像预想的那样发生):
Executing 0 actions on thread 12
Executed, 0 actions left
Executing 0 actions on thread 12
Executed, 0 actions left
Invoking
Enqueued
Waiting on thread 13
Invoking
Enqueued
Waiting on thread 14
Executing 2 actions on thread 12
Actioned
Released
Waited
Actioned
Released
Waited
Executed, 0 actions left
Executing 0 actions on thread 12
Executed, 0 actions left
Executing 0 actions on thread 12
实际问题:我在问什么,在这一点上,是,如果有经验的线程的程序员可以看到一个合乎逻辑的错误类可能有史以来使它阻止,因为我看不到发生这种情况的可能方式。同样,如果你能说明一个阻止它的测试用例,我可能会发现我的错在哪里。 我不知道如何隔离问题。
注意:我敢肯定这是不是真的认为是其特异性的质量问题,但我大多张贴作为帮助一个绝望的哭泣,因为这是爱好编程,我没有同事问。 经过一天的试验和错误,我仍然无法修复它。
重要更新:我刚刚在第一次调用时发生了这个错误,不一定只在第二次调用时发生。因此,它可以真正冻结在调用者本身。但是如何?哪里?
我可以看到,为了明显的原因进入赏金。非常感谢任何人想要解决粗暴的代码,没有精确的问题而不需要奖励。 – Lazlo
在您的Invoke方法中,当!block和Execute方法中类似时,应该保护Actions Queue免受并发访问的影响。您可以始终使用ConcurrentQueue。 – spender
好的,但所有的测试都是通过'block = true'进行的。 – Lazlo