簡體   English   中英

無鎖,等待,獨占訪問方法

[英]Lock-free, awaitable, exclusive access methods

我有一個線程安全類,該類使用需要專門訪問的特定資源。 以我的評估,在Monitor.Enter上阻止各種方法的調用者是沒有意義的。輸入或等待SemaphoreSlim以訪問此資源。

例如,我有一些“昂貴的”異步初始化。 由於多次初始化(從多個線程還是單個線程)沒有意義,因此多個調用應立即返回(甚至引發異常)。 相反,應該創建,初始化然后將實例分發到多個線程。

更新1:

MyClass在兩個方向上都使用兩個NamedPipes InitBeforeDistribute方法並不是真正的初始化,而是正確地在兩個方向上建立連接。 在建立連接之前,使管道可用於N線程是沒有意義的。 設置完成后,多個線程可以發布工作,但是實際上只有一個線程可以讀取/寫入流。 我為這些示例的命名不合理而感到困惑。

更新2:

如果InitBeforeDistribute使用適當的等待邏輯實現了SemaphoreSlim(1, 1) (而不是互鎖的操作拋出異常),那么Add / Do Square方法是否可行? 它沒有鎖時不會拋出冗余異常(例如InitBeforeDistribute中的InitBeforeDistribute )嗎?

以下將是 不好的例子:

class MyClass
{
    private int m_isIniting = 0; // exclusive access "lock"
    private volatile bool vm_isInited = false; // vol. because other methods will read it

    public async Task InitBeforeDistribute()
    {
        if (Interlocked.Exchange(ref this.m_isIniting, -1) != 0)
            throw new InvalidOperationException(
                "Cannot init concurrently! Did you distribute before init was finished?");

        try
        {
            if (this.vm_isInited)
                return;

            await Task.Delay(5000)      // init asynchronously
                .ConfigureAwait(false);

            this.vm_isInited = true;
        }
        finally
        {
            Interlocked.Exchange(ref this.m_isConnecting, 0);
        }
    }
}

一些要點:

  1. 如果存在阻塞/等待訪問鎖的情況很合理的情況,則此示例沒有意義(也就是說)。
  2. 由於我需要在該方法中等待,因此如果要在其中使用“適當”的鎖,則必須使用SemaphoreSlim之類的東西。 放棄上面示例的信號量后,我不必擔心一旦完成處理該類的工作。 (我總是不喜歡處理多個線程使用的項目的想法。當然,這是次要的肯定。)
  3. 如果經常調用該方法,則可能會帶來一些性能上的好處,當然應該對其進行衡量。

上面的示例在參考文獻中沒有任何意義。 至(3.),因此這是另一個示例:

class MyClass
{
    private volatile bool vm_isInited = false; // see above example
    private int m_isWorking = 0; // exclusive access "lock"
    private readonly ConcurrentQueue<Tuple<int, TaskCompletionSource<int>> m_squareWork =
        new ConcurrentQueue<Tuple<int, TaskCompletionSource<int>>();

    public Task<int> AddSquare(int number)
    {
        if (!this.vm_isInited) // see above example
            throw new InvalidOperationException(
                "You forgot to init! Did you already distribute?");

        var work = new Tuple<int, TaskCompletionSource<int>(number, new TaskCompletionSource<int>()
        this.m_squareWork.Enqueue(work);

        Task do = DoSquare();

        return work.Item2.Task;
    }

    private async Task DoSquare()
    {
        if (Interlocked.Exchange(ref this.m_isWorking, -1) != 0)
            return; // let someone else do the work for you

        do
        {
            try
            {
                Tuple<int, TaskCompletionSource<int> work;

                while (this.m_squareWork.TryDequeue(out work))
                {
                    await Task.Delay(5000)      // Limiting resource that can only be
                        .ConfigureAwait(false); // used by one thread at a time.

                    work.Item2.TrySetResult(work.Item1 * work.Item1);
                }
            }
            finally
            {
                Interlocked.Exchange(ref this.m_isWorking, 0);
            }
        } while (this.m_squareWork.Count != 0 &&
            Interlocked.Exchange(ref this.m_isWorking, -1) == 0)
    }
}

我應該注意這個“無鎖”示例的某些特定負面影響嗎?

關於SO上的“無鎖”代碼的大多數問題通常都建議不要這樣做,並指出這是給“專家”的。 很少(我可能在這本書上寫錯了),如果有人傾向於的話,我是否能看到有關書籍/博客/等方面的建議? 如果有任何此類資源可供參考,請分享。 任何建議將不勝感激!

更新:相關的好文章

。:創建高性能鎖和無鎖代碼(用於.NET):。


  1. 關於無lock-free要點不是針對experts
    要點是Do you really need lock-free algorythm here? 我在這里無法理解您的邏輯:

    由於多次初始化(從多個線程還是單個線程)沒有意義,因此多個調用應立即返回 (甚至引發異常)。

    為什么您的用戶不能簡單地等待初始化結果,然后再使用您的資源? 如果可以的話,只需使用Lazy<T>類,甚至使用Asynchronous Lazy Initialization

  2. 您確實應該閱讀有關共識號CAS運算的知識,以及在實現自己的同步原語時為何如此重要。

    在您的代碼中,您使用的是Interlocked.Exchange方法,該方法不是真正的CAS ,因為它始終交換值,並且其共識數等於2 這意味着使用這種構造的原語只能在2線程中正常工作(不是您的情況,而是2 )。

    我試圖定義是您的代碼在3線程中正常工作,還是可能在某些情況下導致您的應用程序進入損壞狀態,但是30分鍾后我停了下來。 經過一段時間的努力,您的任何團隊成員都會像我一樣停下來理解您的代碼。 這不僅浪費時間,而且浪費您的團隊。 除非確實需要,否則不要重新發明輪子。

  3. 我在相關領域中最喜歡的書是Ben Watson 編寫的高性能.NET代碼 ,而我最喜歡的博客是Stephen Cleary的博客。 如果您可以更具體地了解自己感興趣的書籍,則可以添加更多參考資料。

  4. 程序中沒有鎖不會使您的應用程序無lock-free 在.NET應用程序中,實際上不應在內部程序流中使用Exceptions 考慮一下操作系統並沒有安排初始化線程一段時間(出於各種原因,無論它們到底是什么)。

    在這種情況下,應用程序中的所有其他線程將逐步嘗試訪問共享資源而死亡。 我不能說這是無lock-free代碼。 是的,其中沒有鎖,但是不能保證程序的正確性,因此按定義它不是無鎖的

Maurice Herlihy和Nir Shavit撰寫的《多處理器編程的藝術》是無鎖和無等待編程的重要資源。 無鎖是除編程模式以外的一種進度保證,因此要說一種算法是無鎖的,則必須驗證或顯示該進度保證的證明。 簡單地說,無鎖意味着阻塞或停止一個線程不會阻塞其他線程的進度,或者如果一個線程被無限頻繁地阻塞,那么還有另一個線程會無限地頻繁進步。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM