首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >一个并发安全的堆栈结构。

一个并发安全的堆栈结构。
EN

Code Review用户
提问于 2014-04-27 14:24:43
回答 2查看 478关注 0票数 2

这是一个教学示例,旨在说明在F#中可变和列表的使用。该结构是一种线程安全堆栈结构,多个线程可以同时推送到/pop。关切事项:

  • 这真的是线程安全吗?
  • 锁有必要吗?
  • 是否有一种不变的方法来做同样的事情?我不认为在这里可以使用传统的不可变方法,即让Push/Pop返回一个新的堆栈实例,因为不同的客户端会看到不同的堆栈视图。

代码如下所示:

代码语言:javascript
复制
type ConcurrentStack<'T>() =    
    let mutable _stack : List<'T> = []

    member this.Push value =
      lock _stack (fun () -> 
         _stack <- value :: _stack)

    member this.TryPop() =
      lock _stack (fun () ->
         match _stack with
         | result :: newStack ->
            _stack <- newStack
            result |> Some
         | [] -> None
      )
EN

回答 2

Code Review用户

回答已采纳

发布于 2014-04-27 14:59:31

这真的是线程安全吗?

是的,我想会的。对_stack字段的所有访问都受到锁的保护,所以我不明白为什么这不是线程安全。

使用不可变列表的一个很好的巧合是,您还可以很好地实现IEnumerable<'T>,甚至返回堆栈中当前所有元素的列表--因为数据类型是不可变的,所以它可以安全地与其他线程共享(这意味着,您不会遇到"Collection;枚举操作不能继续“的常见麻烦,您可以在C#中获得这些操作。

锁有必要吗?

我也这么想。如果没有锁,一个线程可以通过计算Push开始计算value::_stack,然后另一个线程可以修改_stack,然后原始线程会用旧的value::_stack值覆盖_stack (这样,另一个线程的效果就会消失!)

是否有一种不变的方法来做同样的事情?我不认为在这里可以使用传统的不可变方法,即让Push/Pop返回一个新的堆栈实例,因为不同的客户端会看到不同的堆栈视图。

如果想法是让多个线程共享状态,那么它们就需要某种方式来共享&改变状态。F#中的另一种方法是使用代理并将当前的_stack保持为异步循环的参数--在这种情况下,您不会在编写的F#代码中使用突变(但是代理中存在隐藏的变异和同步)。

从逻辑上讲,基于代理的版本与您使用锁的版本非常相似--由于其他原因(获得正确的锁很难,代理不可能执行相同的错误),这可能会更好一些,但这取决于您想要说明什么!

票数 3
EN

Code Review用户

发布于 2014-04-27 17:01:23

锁有必要吗?

一些同步是必要的,但它不一定是一个锁。

另一种选择是使用Interlocked.CompareExchange。但是这样做要复杂得多,所以我会坚持使用锁,除非分析表明锁是减慢代码速度的原因。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/48335

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档