除了“荣誉代码”之外,使用专用的“锁定对象”和直接链接数据有区别吗?

Posted

技术标签:

【中文标题】除了“荣誉代码”之外,使用专用的“锁定对象”和直接链接数据有区别吗?【英文标题】:Beyond "honor code", is there a difference usign a dedicated "lock object" and locking data directly? 【发布时间】:2017-05-23 05:43:44 【问题描述】:

我有两个线程:一个提供更新,另一个将更新写入磁盘。只有最近的更新很重要,所以我不需要 PC 队列。

简而言之:

feeder 线程将最新更新放入缓冲区,然后设置一个标志以指示新更新。 写入器线程检查标志,如果它指示新内容,则将缓冲的更新写入磁盘并再次禁用标志。

我目前正在使用专用锁定对象来确保没有不一致,我想知道直接锁定标志和缓冲区有什么区别。我唯一知道的是,专用锁对象需要信任每个想要操纵标志和缓冲区的人都使用锁。

相关代码:

private object cacheStateLock = new object();
string textboxContents;
bool hasNewContents;

private void MainTextbox_TextChanged(object sender, TextChangedEventArgs e)

    lock (cacheStateLock)
    
        textboxContents = MainTextbox.Text;
        hasNewContents = true;
    


private void WriteCache() // running continually in a thread

    string toWrite;

    while (true)
    
        lock (cacheStateLock)
        
            if (!hasNewContents)
                continue;

            toWrite = textboxContents;
            hasNewContents = false;
        

        File.WriteAllText(cacheFilePath, toWrite);
    

【问题讨论】:

缓冲区是指textboxContents吗?你是在问锁定textboxContents而不是cacheStateLock是否有效?如果是这样 - 不 - 这是完全无效的,因为 textboxContents 的值在不断变化 嗯,对我来说,锁意味着特定的一段代码(写在锁内)一次被一个线程锁定,不允许其他线程操作里面的数据。 Rob 的意思,我认为这也是你问题的答案,你不能锁定一个变量,只能锁定一个对象。值类型,例如您的布尔标志,没有监视器(锁定所需的元数据)。虽然字符串变量包含一个可以拥有关联监视器的对象,但锁定仅在每个人都获得相同的锁时才有效,并且您的字符串变量不断被重新分配给新的字符串对象,每个对象都是不同的锁。 它在您的代码中不是很明显,但是如果您公开您的数据和/或将其传递给其他代码,则不能保证它也不会尝试锁定它,使用专用私人锁您可以排除它可能性 评论不是为了回答。 @BenVoigt 或其他人会考虑写一个答案吗?? 【参考方案1】:

首先,如果您尝试以这种方式使用bool 标志,则应将其标记为volatile(which isn't recommended at all,但比您的代码更好)。

要注意的第二件事是lock 语句是Monitor 类方法的sintax 糖,所以即使您能够为其提供值类型(顺便说一句,这是一个编译错误) ,两个不同的线程将获得自己的标志版本,使lock 无用。所以you must provide a reference type for lock statement。

第三件事是strings are immutable in the C#,所以理论上某些方法可以存储对字符串的旧引用并以错误的方式执行lock。在您的情况下,字符串也可能从MainTextbox.Text 变成null,这将在运行时抛出,与永远不会改变的private 对象进行比较(顺便说一下,您应该将其标记为readonly)。

因此,为同步引入一个专用对象是将锁定与实际逻辑分开的最简单自然的方法

至于您的初始代码,它有一个问题,因为MainTextbox_TextChanged 可以覆盖没有被写下的文本。您可以在这里引入一些额外的同步逻辑或使用一些库。 @Aron 这里建议Rx,我个人更喜欢TPL Dataflow,没关系。

您可以添加链接到ActionBlock<string>(WriteCache)BroadcastBlock,这将从WriteCache 方法和两个方法中的lock 中删除无限循环:

var broadcast = new BroadcastBlock<string>(s => s);
var consumer = new ActionBlock<string>(s => WriteCache(s));
broadcast.LinkTo(consumer);

// fire and forget
private async void MainTextbox_TextChanged(object sender, TextChangedEventArgs e)

    await broadcast.SendAsync(MainTextbox.Text);


// running continually in a thread without a loop
private void WriteCache(string toWrite)

    File.WriteAllText(cacheFilePath, toWrite);

【讨论】:

以上是关于除了“荣誉代码”之外,使用专用的“锁定对象”和直接链接数据有区别吗?的主要内容,如果未能解决你的问题,请参考以下文章

除了像 onclick 这样的委托处理程序事件之外,Jquery 直接事件不会触发?

为啥 UIView 的扩展对除了放置在 IB 上的 UIView 之外的其他控件没有直接影响?

yarn 安装了 2 个版本的 jquery。为啥,除了直接编辑 yarn.lock 之外,我该如何修复它?

#yyds干货盘点#dart系列之:HTML的专属领域,除了javascript之外,dart也可以

苹果蓝牙耳机亮盒子亮蓝光是啥意思?

除了图像之外,您可以在 Azure devops 中预览工作项附件吗?