何时在C#中使用锁定线程?

时间:2012-01-04 00:47:53

标签: c# multithreading optimization thread-safety

我有一个处理多个传入套接字连接的服务器,并创建了两个不同的线程,以XML格式存储数据。

我几乎在每个名为asyncronously的事件处理程序和代码的不同部分的2个线程中使用lock语句来保证线程安全。遗憾的是,使用这种方法,我的应用程序显着减慢了。

我试图完全不使用lock,服务器的执行速度非常快,即使文件存储似乎也在提升;但程序因30秒-1分钟后我不理解的原因而崩溃。工作。

因此。我认为最好的方法是使用较少的锁或只在必要的地方使用它。因此,我有两个问题:

  1. 只有在我写入公共访问变量(C#列表)时,甚至在我从中读取时,是否需要锁定?

  2. 是否只需要在套接字处理程序或其他地方创建的异步线程中进行锁定?

  3. 有人可以给我一些关于如何操作的实用指南。我这次不会发布整个代码。发布约2500行代码是没有意义的。

5 个答案:

答案 0 :(得分:87)

当没有交叉路口时,您是否曾坐在车上或红灯处的公共汽车上?浪费时间,对吧?锁就像一个完美的交通灯。它总是绿色的,除非交叉路口有交通。

你的问题是“我花了太多时间在红灯等待交通。我应该只是运行红灯吗?或者更好,我应该完全取下灯光,让大家以高速公路的速度开车穿过交叉路口交叉口控制?“

如果您遇到锁定性能问题,那么删除锁定就是您应该做的 last 事情。 您正在等待红灯,因为交叉路口有交叉路口。如果没有争用,锁非常快

在不消除交叉流量的情况下,您无法消除光线。因此,最佳解决方案是消除交叉流量。如果永远不会争论锁定,那么你永远不会等待它。弄清楚为什么交叉流量在交叉路口花费了这么多时间;不要移除光线,希望没有碰撞。会有。

如果你不能这样做,那么添加更细粒度的锁有时会有帮助。也就是说,也许你在城里的每条道路都聚集在同一个十字路口。也许你可以把它分成两个交叉点,这样代码就可以同时在两个不同的交叉点上移动。

请注意,使汽车更快(获得更快的处理器)或缩短道路(消除代码路径长度)通常会使问题在多线程场景中变得更糟。就像在现实生活中一样;如果问题是僵局,那么购买更快的汽车并在较短的道路上驾驶它们会使他们更快地堵塞交通堵塞,但不会更快。

答案 1 :(得分:33)

  

当我写入公共访问变量(C#列表)时,甚至当我从中读取时,是否需要锁定?

是的(即使你读过)。

  

锁只需要在套接字处理程序或其他地方创建的异步线程中吗?

是。只要代码访问共享的代码段,总是锁定。


这听起来好像你可能没有锁定个人对象,而是锁定一件事来锁定所有

如果是这样,通过创建关联的单独的唯一对象并且一次只锁定某些部分,这样就不会干扰其他线程的智能离散锁定部分。

以下是一个例子:

// This class simulates the use of two different thread safe resources and how to lock them
// for thread safety but not block other threads getting different resources.
public class SmartLocking
{
    private string StrResource1 { get; set; }
    private string StrResource2 { get; set; }

    private object _Lock1 = new object();
    private object _Lock2 = new object();

    public void DoWorkOn1( string change )
    {
        lock (_Lock1)
        {
            _Resource1 = change;
        }
    }

    public void DoWorkOn2( string change2 )
    {
        lock (_Lock2)
        {
            _Resource2 = change2;
        }
    }
}

答案 2 :(得分:2)

访问成员(读取或写入)时始终使用锁定。如果你正在迭代一个集合,而另一个线程你正在删除项目,那么事情很快就会出错。

建议是,您希望迭代集合,将所有项目复制到新集合,然后迭代副本。即。

var newcollection; // Initialize etc.
lock(mycollection)
{
  // Copy from mycollection to newcollection
}

foreach(var item in newcollection)
{
  // Do stuff
}

同样,只有在您实际写入列表时才使用锁定。

答案 3 :(得分:1)

您在阅读时需要锁定的原因是:

假设您正在对一个属性进行更改,并且当线程处于锁定之间时,它已被读取两次。在我们做出任何改变之前和之后,我们将得到不一致的结果。

我希望有所帮助,

答案 4 :(得分:0)

基本上这可以很简单地回答:

您需要锁定不同线程访问的所有内容。如果关于阅读或写作,它实际上并不重要。如果您正在阅读并且另一个线程正在覆盖数据,同时读取的数据可能会无效并且您可能正在执行无效操作。