拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 如何在C#中原子地执行代码块

如何在C#中原子地执行代码块

白鹭 - 2022-01-24 2104 0 0

我有一个多执行绪 UI 应用程序,它启动多个后台执行绪。许多这些执行绪执行如下所示的代码:

public void Update(){
   if(Dispatcher.HasShutdownStarted()) return;
   Dispatcher.Invoke(()=>{...});
...
}

然后我有时可能会有一个执行绪执行下面的代码

pubic void Shutdown(){
   if(Dispatcher.HasShutdownStarted()) return;
   Dispatcher.InvokeShutdown();
}

问题是有时一个执行绪在Dispatcher.InvokeShutdown()另一个执行绪执行之后Dispatcher.HasShutdwonStarted()但在它到达之前执行Dispatcher.Invoke(()=>{...})这意味着,一旦 Dispatcher 开始关闭,就会有一个执行绪尝试在 Dispatcher 上执行 lambda。那就是我遇到例外的时候。对此的最佳解决方案是什么?

uj5u.com热心网友回复:

您面临的问题是在执行 Invoke 中的代码之前检查了 HasShutdownStarted(因为它在调度程序上排队)

我认为更好的方法是在呼叫内部检查它,这样你就不需要任何锁。

public void Update(){
   Dispatcher.Invoke(()=>
   {
       if(Dispatcher.HasShutdownStarted()) return;
       ...
   });
}

uj5u.com热心网友回复:

一种选择是使用任何物件创建同步块,例如:

// Define property
private readonly object _thisLock = new object();

然后在你只需要一个执行绪的代码中使用synchronized关键字

    public void SetTps(decimal limit)
    {
        if (_limit == limit || limit <= 0)
            return;
        // This is the important part
        lock (_thisLock)
        {
            _limit = limit;
            ResetTps();
        }
    }    

锁有点重,您也可以尝试使用semaphore 或 semaphore slim

// First you create the semaphore
semaphore = new SemaphoreSlim(0, 3);
// Then you solicit with wait
semaphore.Wait();
// Do all your work that needs only one resource at the time
// After you release the semaaphore
semaphore.Release();

编辑

几乎忘记了,对于您的情况,在读取次数很多而写入次数不多的情况下,您可以使用ReaderWriterLockSlim

public class SynchronizedCache 
{
    private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
    private Dictionary<int, string> innerCache = new Dictionary<int, string>();

    public int Count
    { get { return innerCache.Count; } }

    public string Read(int key)
    {
        cacheLock.EnterReadLock();
        try
        {
            return innerCache[key];
        }
        finally
        {
            cacheLock.ExitReadLock();
        }
    }

    public void Add(int key, string value)
    {
        cacheLock.EnterWriteLock();
        try
        {
            innerCache.Add(key, value);
        }
        finally
        {
            cacheLock.ExitWriteLock();
        }
    }

    public bool AddWithTimeout(int key, string value, int timeout)
    {
        if (cacheLock.TryEnterWriteLock(timeout))
        {
            try
            {
                innerCache.Add(key, value);
            }
            finally
            {
                cacheLock.ExitWriteLock();
            }
            return true;
        }
        else
        {
            return false;
        }
    }

    public AddOrUpdateStatus AddOrUpdate(int key, string value)
    {
        cacheLock.EnterUpgradeableReadLock();
        try
        {
            string result = null;
            if (innerCache.TryGetValue(key, out result))
            {
                if (result == value)
                {
                    return AddOrUpdateStatus.Unchanged;
                }
                else
                {
                    cacheLock.EnterWriteLock();
                    try
                    {
                        innerCache[key] = value;
                    }
                    finally
                    {
                        cacheLock.ExitWriteLock();
                    }
                    return AddOrUpdateStatus.Updated;
                }
            }
            else
            {
                cacheLock.EnterWriteLock();
                try
                {
                    innerCache.Add(key, value);
                }
                finally
                {
                    cacheLock.ExitWriteLock();
                }
                return AddOrUpdateStatus.Added;
            }
        }
        finally
        {
            cacheLock.ExitUpgradeableReadLock();
        }
    }

    public void Delete(int key)
    {
        cacheLock.EnterWriteLock();
        try
        {
            innerCache.Remove(key);
        }
        finally
        {
            cacheLock.ExitWriteLock();
        }
    }

    public enum AddOrUpdateStatus
    {
        Added,
        Updated,
        Unchanged
    };

    ~SynchronizedCache()
    {
       if (cacheLock != null) cacheLock.Dispose();
    }
}

对于您的情况,它看起来像:

public void Update(){
   cacheLock.EnterReadLock();
   try {
      if(Dispatcher.HasShutdownStarted()) return;
   } finally {
      cacheLock.ExitReadLock();
   }
   Dispatcher.Invoke(()=>{...});
...
}

pubic void Shutdown(){
   cacheLock.EnterWriteLock();
   try {
      if(Dispatcher.HasShutdownStarted()) return;
      Dispatcher.InvokeShutdown();
   } finally {
      cacheLock.ExitWriteLock();
   }
}
标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *