跨线程/原子检查同步?(Synchronization across threads / atomi

2019-09-30 03:14发布

我需要创建一个方法调用,任何线程(例如起见线程B)可以调用,这将在其执行一个特定的给定点处的主要执行的线程(THEAD A)上执行。

示例性的使用情况如下:

static Invoker Invoker = new Invoker();

static void ThreadA()
{
    new Thread(ThreadB).Start();

    Thread.Sleep(...); // Hypothetic Alpha

    Invoker.Invoke(delegate { Console.WriteLine("Action"); }, true);

    Console.WriteLine("Done");

    Console.ReadLine();
}

static void ThreadB()
{
    Thread.Sleep(...); // Hypothetic Beta

    Invoker.Execute();
}

调用程序类看起来是这样的:

public class Invoker
{
    private Queue<Action> Actions { get; set; }

    public Invoker()
    {
        this.Actions = new Queue<Action>();
    }

    public void Execute()
    {
        while (this.Actions.Count > 0)
        {
            this.Actions.Dequeue()();
        }
    }

    public void Invoke(Action action, bool block = true)
    {
        ManualResetEvent done = new ManualResetEvent(!block);

        this.Actions.Enqueue(delegate
        {
            action();
            if (block) done.Set();
        });

        if (block)
        {
            done.WaitOne();
        }
    }
}

这在大多数情况下细,虽然它不会,如果因为任何原因,执行(并因此Set )是之前完成WaitOne ,在这种情况下,它只是冻结(它允许线程继续进行,然后块)。 这可能如果阿尔法贝塔>>转载。

我可以使用布尔和诸如此类的东西,但我从来没有在这里得到真正的原子安全。 我尝试了一些修正,但他们不会在Beta版>>阿尔法的情况下工作。

我也想过周围既Invoker.Execute和Invoker.Invoke方法锁定,使我们可以保证的是,执行不和入队等待之间发生的。 然而,问题是,锁也englobes的WaitOne的,因此无法完成(僵局)。

我应该如何去在这种模式得到绝对的安全性原子?

注意:这真的是我这个设计工作,从外部依赖的要求。 因此,改变设计是不是一个真正的选择。

编辑 :我忘了提,我希望有一个阻塞行为(基于bool block ,直至委托上调用调用执行)。

Answer 1:

使用Semaphore(Slim) ,而不是ManualResetEvent

创建的最大计数信号量1 ,调用WaitOne()调用线程,并调用Release()的委托。

如果您已经叫Release() WaitOne()应立即返回。

确保Dispose()是当你做,最好是在using块。
如果block是假的,你不应该摆在首位创建它(尽管SemaphoreSlim ,这不是那么糟糕)。



Answer 2:

你可以使用我的技术 :

public void BlockingInvoke(Action action)
{
    volatile bool isCompleted = false;
    volatile bool isWaiting = false;
    ManualResetEventSlim waiter = new ManualResetEventSlim();

    this.Actions.Enqueue(delegate
    {
        action();

        isCompleted = true;
        Thread.MemoryBarrier();
        if (!isWaiting) 
            waiter.Dispose();
        else
            waiter.Set();
    });

    isWaiting = true;
    Thread.MemoryBarrier();
    if (!isCompleted)
        waiter.Wait();
    waiter.Dispose();
}

未经测试



Answer 3:

我只回答表明所描述的实施SLaks和我的解决方案,以确保适当的和独特的处理与锁。 它是开放的改善和批评,但它的实际工作。

public class Invoker
{
    private Queue<Action> Actions { get; set; }

    public Invoker()
    {
        this.Actions = new Queue<Action>();
    }

    public void Execute()
    {
        while (this.Actions.Count > 0)
        {
            this.Actions.Dequeue()();
        }
    }

    public void Invoke(Action action, bool block = true)
    {
        if (block)
        {
            SemaphoreSlim semaphore = new SemaphoreSlim(1);
            bool disposed = false;

            this.Actions.Enqueue(delegate
            {
                action();
                semaphore.Release();

                lock (semaphore)
                {
                    semaphore.Dispose();
                    disposed = true;
                }
            });

            lock (semaphore)
            {
                if (!disposed)
                {
                    semaphore.Wait();
                    semaphore.Dispose();
                }
            }
        }
        else
        {
            this.Actions.Enqueue(action);
        }
    }
}


文章来源: Synchronization across threads / atomic checks?