【问题标题】:Thread synchronization over a static lock when accessing a shared resource访问共享资源时通过静态锁进行线程同步
【发布时间】:2014-07-24 14:52:22
【问题描述】:

我正在尝试解决两个线程的线程同步问题,这两个线程都访问一个资源。在此示例中,该资源是 Engine

这里两次启动正在启动引擎,其中一个线程正在停止它。 目标是得到引擎启动的最终结果

澄清一下:我不控制 ThreadOne/ThreadTwo 中的代码,并且需要在 Lifecycle 类中进行同步。

在此示例中实现这一目标的最佳方法是什么?

using System;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        new Thread(ThreadOne).Start();
        new Thread(ThreadTwo).Start();

        Console.Read();
    }

    private static void ThreadOne(object obj)
    {
        Lifecycle.Start();
        Thread.Sleep(500);
        Lifecycle.Stop();
    }

    private static void ThreadTwo(object obj)
    {
        Thread.Sleep(600);
        Lifecycle.Start();
    }
}

class Engine
{
    public void Start()
    {
        Console.WriteLine("Engine was started");
    }

    public void Stop()
    {
        Console.WriteLine("Engine was stopped");
    }
}

static class Lifecycle
{
    private static readonly object LockObject;
    private static Engine Engine;

    static Lifecycle()
    {
        LockObject = new object();
        Engine = new Engine();
    }

    public static void Start()
    {
        lock (LockObject)
        {
            Engine.Start();
            Thread.Sleep(800);
        }
    }

    public static void Stop()
    {
        lock (LockObject)
        {
            Engine.Stop();
        }
    }
}

【问题讨论】:

  • 您希望最后一件事是线程 2 启动引擎吗?
  • 但是线程2有可能在线程1执行Stop方法之前运行Start吗?
  • @YuvalItzchakov 是的,是的。在此示例中,它将启动引擎两次,然后停止一次。而不是开始、停止、开始
  • 从外观上看,您希望所有操作按顺序发生。在您的程序中,从来没有任何时间点每个线程都应该同时做某事,您也不希望任何动作交织在一起。因此,解决方案显然是删除所有额外的线程并按顺序运行程序。您添加了一堆线程开销,实际上是零收益。
  • 谢谢大家,你们的回答,特别是删除的回答很有帮助。

标签: c# multithreading


【解决方案1】:

为什么不用直接访问 Monitor 的方法来替换 locks?

internal static class Lifecycle
{
    private static Engine Engine;

    static Lifecycle()
    {
        Engine = new Engine();
    }

    public static void Start()
    {
        Monitor.Enter(Engine);
        Engine.Start();
        Thread.Sleep(800);
    }

    public static void Stop()
    {
        Engine.Stop();
        Monitor.Exit(Engine);
    }
}

【讨论】:

  • @scott4dev - 当然,您可以锁定 Lifecycle 类型。但是,如果您出于某种原因需要多个锁,则不会这样做。
  • @ChristopherCurrens-MSFT 他不建议锁定LifeCycle。那将是一个可怕的想法。他建议锁定Engine,这非常不同。
  • @Servy - 谢谢,直到我看不懂。这更有意义,我没有看到很多人建议锁定类型(获得死锁情况的简单方法)。
  • @ChristopherCurrens-MSFT 现在你的解决方案和我的一样,你稍后再发布
  • @scott4dev - 我帮不了你(甚至不能删除它,因为它被标记为答案)。请 OP 标记您的。这些点对我来说没有任何意义。
【解决方案2】:

如果您的目标是多次调用Start 而不实际启动第二次

private static bool _isStarted

public static void Start()
{
   if(!_isStarted)
   {
       _isStarted = true;
       ...
   }
}

// set to false in Stop()

这有竞争条件问题。我用简单的变量来给出一个想法。

如果你的目标是调用最后一个,那么它就像这样简单地实现:

static int _counter;

// thread 1
Interlocked.Increment(ref _counter); // instead of start
... // sleep?
Incterlocked.Decrement(ref _counter); // instead of stop

// thread 2
Interlocked.Increment(ref _counter); // instead of start
... // sleep?

// after all threads are finished, check _counter
// positive - means Start()

【讨论】:

  • 不,目标是执行Start,Stop,Start,一个都不漏。
  • 我现在很困惑你需要什么。在 cmets 你说In this example, it'll start the engine twice, and then stop it once. Rather than Start, Stop, Start 这让我觉得你在用不必要的电话来对抗开销。但是现在您声明您不想错过电话。你想给他们排队还是什么?
【解决方案3】:

现在 Lifecycle 管理同步

using System;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        new Thread(ThreadOne).Start();
        new Thread(ThreadTwo).Start();

        Console.Read();
    }

    private static void ThreadOne(object obj)
    {
        Lifecycle.Start();
        Thread.Sleep(500);
        Lifecycle.Stop();
    }

    private static void ThreadTwo(object obj)
    {
        Thread.Sleep(600);
        Lifecycle.Start();
    }
}

class Engine
{
    public void Start()
    {
        Console.WriteLine("{0:O} [{1}] Engine was started", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
    }

    public void Stop()
    {
        Console.WriteLine("{0:O} [{1}] Engine was stopped", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
    }
}

static class Lifecycle
{
    private static Engine Engine;

    static Lifecycle()
    {
        Engine = new Engine();
    }

    public static void Start()
    {
        Monitor.Enter(Engine);
        Engine.Start();
        Thread.Sleep(800);
    }

    public static void Stop()
    {
        Engine.Stop();
        Monitor.Exit(Engine);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多