4

我在 .NET 4.5 - Web API Self Host 上使用 C#。服务器端我有一个不是线程安全的进程:它一次只能处理一个请求。如何在控制器代码中锁定此资源(进程),以便按顺序为客户端提供服务,等待资源在使用前被释放?就像是:

while(true){
    if(!process.isLocked)
        break;
}

lock(process)
do(work)
unlock(process)
warn(others)

任何代码片段或建议表示赞赏。提前致谢。

4

2 回答 2

5

如果您正在寻找要执行的每个线程,但一次只执行一个,那么您可以将lock语句与静态对象一起使用:

private static object lockobj = new object();

public void DoWorkWhenNotBusy()
{
    lock (lockobj)
    {
        // do work
        Console.WriteLine("Working #1 (should always complete)...");
        Thread.Sleep(500);
    }
}

如果您希望线程在对象被锁定时立即返回,那么您可以这样写(双重检查锁定):

private static object lockobj2 = new object();
private volatile bool locked = false;

public void DoWorkIfNotBusy()
{
    if (!locked)
    {
        lock (lockobj2)
        {
            if (!locked)
            {
                locked = true;
                // do work
                Thread.Sleep(500);
                Console.WriteLine("Working #2 (only if not busy)...");
            }
            locked = false;
        }
    }
}

测试示例:

for (int i = 0; i < 10; i++)
{
    var ts = new ThreadStart(DoWorkWhenNotBusy);
    Thread t = new Thread(ts);
    t.Start();

    var ts2 = new ThreadStart(DoWorkIfNotBusy);
    Thread t2 = new Thread(ts2);
    t2.Start();

    Console.WriteLine("{0} started", i);
}
于 2012-11-22T00:08:12.520 回答
3

我想在这里扩展 dbaseman 的答案。具体来说,他的DoWorkIfNotBusy方法。

我认为这是一个不好的例子(由于可能的竞争条件),并且想展示我认为正确的方式是:

private static readonly object lockobj = new object();

public bool DoWorkIfNotBusy()
{
    bool lockWasTaken = false;
    var temp = lockobj;
    try
    {
        Monitor.TryEnter(temp, ref lockWasTaken);

        if (lockWasTaken) // This crucial test was missing! (Added 2021-04-08)
        {
            //Do work here.. 
        }
    }
    finally
    {
        if (lockWasTaken) Monitor.Exit(temp);
    }
    return lockWasTaken;
}

2021-04-08 更新:增加了当前线程是否成功获取锁的测试。如果不是,则不应执行关键部分中的代码。


2021-04-09 更新:下面的代码显示需要检查lockWasTaken,作为单元测试。

[TestClass]
public class DummyTests
{
    private static readonly object LockObj = new object();

    [TestMethod]
    public void TestMonitor()
    {
        Thread trd1 = StartThread();
        Thread.Sleep(100);
        Thread trd2 = StartThread();
        Thread.Sleep(1000);
        Thread trd3 = StartThread();

        while (trd1.IsAlive || trd2.IsAlive || trd3.IsAlive)
        {
            Thread.Sleep(100);
        }
    }

    private Thread StartThread()
    {
        var thread = new Thread(parameter => this.ThreadTask());
        thread.Start(nameof(thread));
        Trace.WriteLine($"Started thread {thread.ManagedThreadId}.");
        return thread;
    }

    private void ThreadTask()
    {
        bool lockAcquired = false;
        try
        {
            Monitor.TryEnter(LockObj, ref lockAcquired);
            if (lockAcquired)
            {
                Trace.WriteLine($"Lock acquired by thread {Thread.CurrentThread.ManagedThreadId}.");
                Thread.Sleep(1000);
            }
            else
            {
                Trace.WriteLine($"Lock denied to thread {Thread.CurrentThread.ManagedThreadId}.");
            }
        }
        finally
        {
            if (lockAcquired)
            {
                Monitor.Exit(LockObj);
                Trace.WriteLine($"Lock released by thread {Thread.CurrentThread.ManagedThreadId}.");
            }
        }
    }
}

输出将是这样的:

Started thread 12.
Lock acquired by thread 12.
Started thread 13.
Lock denied to thread 13.
Lock released by thread 12.
Started thread 14.
Lock acquired by thread 14.
Lock released by thread 14.
于 2012-11-22T00:57:47.797 回答