在同步块内部和外部使用字段是否安全?

时间:2015-02-25 09:40:16

标签: c# .net multithreading resharper

背景

我们的应用会发送在数据库表中排队的电子邮件。我们发送了一些重复电子邮件的实例,因此我实施了一个锁定,以防止多个线程同时发送电子邮件。

ReSharper警告我:

  

该字段有时在同步块内使用   没有同步使用

问题

为什么ReSharper告诉我这个,为什么我会担心呢?

代码

这是我的(删节)代码:

private readonly IMailQueueRepository _mailQueueRepository = new MailQueueRepository();
private static object _messageQueueLock = new object();

public void SendAllQueuedMessages(IPrincipal caller)
{
    lock (_messageQueueLock) // Prevent concurrent callers
    {
        var message = _mailQueueRepository.GetUnsentMessage();
        while (message != null)
        {
            SendQueuedMessage(message);
            message = _mailQueueRepository.GetUnsentMessage();
        }
    }
}

public void SendQueuedMessage(IMessage message)
{
    // I get the ReSharper warning here on _mailQueueRepository
    var messageAttachments = _mailQueueRepository.GetMessageAttachments(message.Id);
    // etc.
}

2 个答案:

答案 0 :(得分:5)

ReSharper无法告诉(或保证)SendQueuedMessage()仅在同步块内调用。因此,就其而言,其他代码可能会在没有同步的情况下调用SendQueuedMessage()_mailQueueRepository中正在使用SendQueuedMessage()

如果您确定没有其他代码(在包含类的内部或外部)调用此方法,或者您已确保所有来自类SendQueuedMessage()的调用也已同步使用相同的锁定对象,你还好。如果你班级以外没有其他代码真的需要这种方法,我建议你把它变成私有。

答案 1 :(得分:4)

问题场景:

  

我们发送了一些重复电子邮件的实例,因此我正在实施一个锁定,以防止多个线程同时发送电子邮件。

所以你使用Lock()来防止这种情况发生,这意味着你需要同步访问公共资源的线程,在这种情况下_mailQueueRepository

但是,在同一代码中,您使用的_mailQueueRepository没有Lock

 // I get the ReSharper warning here on _mailQueueRepository
    var messageAttachments = _mailQueueRepository.GetMessageAttachments(message.Id); // <== Accessed without a lock

所以这是一个警告,告诉您有价值的资源是以两种不同的形式访问的:一个是synchronized(线程安全),另一个是non-synchronized(非线程安全)。

这是一个警告,让 知道(或让你识别)资源_mailQueueRepository的这种矛盾使用可能引起的问题。您可以选择使用_mailQueueRepository synchronized的所有用法(与lock一起使用并且警告将消失)或管理不竞争条件。

此外,您可能会考虑重新构建代码,以便使用从SendQueuedMessage()中提取的参数来调用_mailQueueRepository,以避免混合使用。