4

.NET 4.5でC#を使用しています-WebAPIセルフホスト。サーバー側スレッドセーフではないプロセスがあります。一度に1つのリクエストを処理できます。このリソース(プロセス)をコントローラーコードでロックして、クライアントに順番にサービスを提供し、リソースが解放されるのを待ってから使用できるようにするにはどうすればよいですか?何かのようなもの:

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

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

コードスニペットや提案は大歓迎です。前もって感謝します。

4

2 に答える 2

5

実行する各スレッドを探しているが、一度に1つしかない場合は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 に答える