c# - 服务中的线程安全设置为 ConcurrencyMode.Multiple

标签 c# multithreading wcf

我正在开发一个 WCF 应用程序,该应用程序托管一个自定义对象供许多客户端访问。它基本上可以工作,但因为我需要处理数以千计的并发客户端,所以我需要该服务能够处理并发读取调用(更新频率不高)。我通过在更新对象时锁定私有(private)字段来添加一些线程安全。

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public sealed class TestServerConfig : ConfigBase, ITestInterface
{
    private object updateLock = new object();

    private SortedList<string, DateTime> dates = new SortedList<string, DateTime>();

    public DateTime GetDate(string key)
    {
        if (this.dates.ContainsKey(key))
        {
            return this.dates[key];
        }
        else
        {
            return DateTime.MinValue;
        }
    }
    public void SetDate(string key, DateTime expirationDate)
    {
        lock (this.updateLock)
        {
            if (this.dates.ContainsKey(key))
            {
                this.dates[key] = expirationDate;
            }
            else
            {
                this.dates.Add(key, expirationDate);
            }
        }
    }
}

我的问题是如何在不锁定的情况下使 GetDate 线程安全,以便可以执行对 GetDate 的并发调用,但在检查之后但读取值之前从集合中删除值时,不会随机发生异常。

捕获异常并处理它是可能的,但我更愿意保留它。

有什么想法吗?

最佳答案

有专门为此设计的锁,ReaderWriterLockSlim (ReadWriterLock 如果您使用的是低于 .NET 4.0 的版本)

此锁允许并发读取,但会在发生写入时锁定读取(和其他写入)。

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public sealed class TestServerConfig : ConfigBase, ITestInterface
{
    private ReaderWriterLockSlim updateLock = new ReaderWriterLockSlim();

    private SortedList<string, DateTime> dates = new SortedList<string, DateTime>();


    public DateTime GetDate(string key)
    {
        try
        {   
            this.updateLock.EnterReadLock();
            if (this.dates.ContainsKey(key))
            {
                return this.dates[key];
            }
            else
            {
                return DateTime.MinValue;
            }
        }
        finally
        {
            this.updateLock.ExitReadLock();
        }
    }
    public void SetDate(string key, DateTime expirationDate)
    {
        try
        {
            this.updateLock.EnterWriteLock();

            if (this.dates.ContainsKey(key))
            {
                this.dates[key] = expirationDate;
            }
            else
            {
                this.dates.Add(key, expirationDate);
            }
        }
        finally
        {
             this.updateLock.ExitWriteLock();
        }
    }
}

还有支持超时的锁的“尝试”版本,您只需检查返回的 bool 值,看看您是否拿走了锁。


更新:另一种解决方案是使用 ConcurrentDictionary ,这根本不需要任何锁。 ConcurrentDictionary 在内部使用锁,但它们的生命周期比您可以使用的锁的生命周期短,而且 Microsoft 也有可能使用某种形式的不安全方法来进一步优化它,我不知道他们在内部采用哪种锁。

不过你需要做一些重写以使你的操作原子化

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public sealed class TestServerConfig : ConfigBase, ITestInterface
{
    private ConcurrentDictionary<string, DateTime> dates = new ConcurrentDictionary<string, DateTime>();

    public DateTime GetDate(string key)
    {
        DateTime result;

        if (this.dates.TryGetValue(key, out result))
        {
            return result;
        }
        else
        {
            return DateTime.MinValue;
        }
    }
    public void SetDate(string key, DateTime expirationDate)
    {
        this.dates.AddOrUpdate(key, expirationDate, (usedKey, oldValue) => expirationDate);
    }
}

更新 2: 出于好奇,我深入了解 ConcurrentDictionary 做了什么,它所做的只是锁定元素的一组桶,所以如果两个对象也共享相同的哈希桶锁,您只会发生锁争用。

通常有 Environment.ProcessorCount * 4 锁桶,但您可以使用设置 concurrencyLevel 的构造函数手动设置它.

这是它如何决定使用哪个锁

private void GetBucketAndLockNo(int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount)
{
    bucketNo = (hashcode & 2147483647) % bucketCount;
    lockNo = bucketNo % lockCount;
}

lockCount 等于构造函数中设置的 concurrencyLevel

关于c# - 服务中的线程安全设置为 ConcurrencyMode.Multiple,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17432623/

相关文章:

c# - 错误 : NU1100: Unable to resolve 'MicrosoftOfficeCore (>= 15.0.0)' for 'net5.0'

c# - Cookie 总是过期

java - 领域驱动设计——CQRS + ES的使用

java - 使用 TaskExecutor 时出现 AssertionError

java - 递增线程名称

.net - 无法使用权​​限 '*' 为 SSL/TLS 建立安全通道

swift - 在 Swift 中进行身份验证的 Azure Api 应用

c# - 如何停止浏览器存储用户在文本字段中输入的表单数据

java - 为什么需要以毫秒为单位的超时以及为什么我必须为单独的 block 声明两个具有单独 run () 方法的类

asp.net - 切换到 SSL 时 WCF 中的访问被拒绝(ASP.NET Forms 身份验证)