简单的线程问题,锁定对共享资源或整个函数的访问?

发布于 2024-10-15 20:30:17 字数 798 浏览 1 评论 0原文

这是我之前的一个问题的解释。这是一个简单的线程问题,但我似乎无法理解。

如果我有共享代码:

private static object objSync = new object();
private int a = 0;

    public void function()
    {
    lock(objSync)
      a += 2;

    lock(objSync)
      a += 3;
    Console.WriteLine(a.ToString());
    a=0;
    }

我不能期望“a”最终等于 5,因为第一个线程获得锁,将“a”设置为 2,然后下一个线程可以获得锁,然后将其设置为“4”第一个线程可以加 3,最后得到 7。

据我了解,解决方案是在整个事物周围放置一个锁,然后您总是可以期望 5。现在我的问题是,如果两个锁之间有一百万行代码怎么办?我无法想象对一百万行代码加锁。如何通过在大约一百万零两行代码中放置一个锁来确保线程安全,而又不会导致性能下降?

编辑:

这是我为这个问题编写的无意义代码。实际应用是线路监控系统。有两个屏幕显示当前线路,一个用于职员,一个用于公众。职员的屏幕通过串行端口接受“点击”,该串行端口使行前进一位,然后通过通知事件更新公共屏幕(请参阅观察者设计模式)。问题是,如果您向答题器发送垃圾邮件,它们不会同步。我想发生的事情是第一个屏幕添加行号,显示它,然后更新公共屏幕,但在公共屏幕有机会显示数据库中的行号之前,职员再次单击,号码就消失了同步。上面的“a”代表我从数据库检索的值,而不仅仅是一个简单的整数。我在一些地方更改了值,并且需要所有这些都以原子方式发生。

This is a paraphrasing of a question I had before. It's a simple threading question but I can't seem to understand it.

If i have shared code:

private static object objSync = new object();
private int a = 0;

    public void function()
    {
    lock(objSync)
      a += 2;

    lock(objSync)
      a += 3;
    Console.WriteLine(a.ToString());
    a=0;
    }

I can't expect 'a' to equal 5 in the end because the first thread gains a lock sets 'a' to 2 and then then the next thread can get a lock set it to '4' before the first thread is able to add 3 and you'll end up with 7 in the end.

The solution as I understand it would be to put a lock around the entire thing and then you could always expect 5. Now my question is what if between the two locks there is a a million lines of code. I can't imagine putting a lock around a million lines of code. How would you ensure thread safety but not take the fail to performance by putting a lock around a million and two lines of code?

EDIT:

This is nonsense code that I wrote for the question. The actual application is a line monitoring system. There are two screens that show the current line, one for the clerks and one for the public. The screen for the clerk accepts 'clicks' through the serial port which progresses the line by one and then updates the public screen through a notify event (see observer design pattern). The problem is they aren't synchronized if you spam the clicker. I imagine what's happening is the first screen adds to the line number, displays it and then updates the public screen, but before the public screen has a chance to show the line number from the database, the clerk clicks again and the number goes out of synch. The 'a' above represents a value I retrieve from the db and not just a simple int. There are a few places that I change the value and I need all of it to happen atomically.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夜灵血窟げ 2024-10-22 20:30:17

这完全取决于您对“成功”的定义。如果它是一个计数器,那仍然可能是正确的结果。如果您只想更新它(如果它是您所想的那样),那么请在第一个锁内拍摄快照,并同样与最终锁中的快照进行比较。在这种情况下,您可以用一些 Interlocked.CompareExchange 替换大部分代码。同样,在“计数器”场景中,Interlocked.Increment 是您的朋友。

如果代码必须匹配,那么您有几个选择:

  • 如果比较失败,则再次运行整百万行;重复直到赢得比赛(通过匹配)
  • 如果比较失败,则抛出
  • 百万行的异常块;它可能仍然是快速的百万行...
  • 考虑一些其他对您的场景有意义的解决策略

在前两个中,您应该通过将任何相关数据保留在中间状态来观察污染的情况。

顺便说一句;最终对 0 的赋值可能也应该是同一锁定策略的一部分。仅当所有访问都遵循锁时,锁才起作用。

但只有您的场景才能告诉我们冲突中“正确”的行为是什么。

It all depends on what you define as "success". If it is a counter, that could still be the right result. If you only want to update it if it is what you thought it was, then take a snapshot inside the first lock, and likewise compare to the snapshot in the final lock. In that scenario you can replace much of the code with some Interlocked.CompareExchange. Likewise in the "counter" scenario, Interlocked.Increment is your friend.

If the code must match, then you have a few options:

  • if the compare fails, run the entire million lines again; repeat until you win the race (by matching)
  • if the compare fails, throw an exception
  • block for the million lines; it might still be a fast million lines...
  • think of some other resolution strategy that makes sense for your scenario

In the first two you should watch for polluting things by leaving any related data in an intermediate state.

And btw; the final assignment to 0 should probably also be part of the same locking strategy. Locks only work if all access respects them.

But only your scenario can tell us what the "right" behaviour in a conflict is.

天冷不及心凉 2024-10-22 20:30:17

这实际上是一个设计问题。如果您的方法根据某些情况计算“a”的不同值,但您不希望另一个线程在操作处于活动状态时更改“a”的值,则如下更合适:

private static object objSync = new object();
private int a = 0;

    public void function()
    {
      int b;
      lock(objSync)
        b = a;
      b += 2;

      //million lines of code

      b +=3;
      lock(objSync)
      {
        a = b;
        Console.WriteLine(a.ToString());
        a=0;
      }
    }

It is a matter of design actually. If your method calculates different values for "a", depending on certain circumstances, but you do not want another thread changing "a"'s value while an operation is active, something like this is more appropriate:

private static object objSync = new object();
private int a = 0;

    public void function()
    {
      int b;
      lock(objSync)
        b = a;
      b += 2;

      //million lines of code

      b +=3;
      lock(objSync)
      {
        a = b;
        Console.WriteLine(a.ToString());
        a=0;
      }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文