从一个线程读取变量并从另一个线程写入变量的正确方法?

时间:2019-11-02 10:22:38

标签: c# multithreading

我正在尝试创建具有2个线程的应用程序。在主线程中的哪里有一个变量(Int32),我只能从主线程中读取它。

现在,只要程序正在运行,我就有另一个线程循环运行,该线程会创建一个“心跳”,当达到某个点时,我想更新上述变量。该变量仅从该检测信号线程写入,而从其他线程都不写入。

为了保持精度(QueryPerformanceCounter级别),我想避免心跳线程上的任何可能阻塞。

这是简化版:

using System;
using System.Threading;

namespace ConsoleApp2
{
    class Program
    {
        static int Interval = 0;

        static void Heartbeat()
        {
            int counter = 0;
            while (counter < 100) {
                counter++;
                Console.WriteLine($"Heartbeat : {counter}");
                if (counter % 10 == 0) {
                    Interval = counter;
                }
                Thread.Sleep(100);
            }
        }

        static void Main(string[] args)
        {
            Thread heartbeat = new Thread(Heartbeat);
            heartbeat.Start();

            while (Interval < 100) {
                Console.WriteLine($"Interval  : {Interval}");
                Thread.Sleep(100);
            }

            Console.ReadLine();
        }
    }
}

它运行了,但是我想知道我的做法是否正确。如果没有,我应该如何改善?

总结:

  1. Interval仅由主线程读取
  2. Interval仅由Heartbeat线程修改。
  3. 我想避免对Heartbeat线程的任何阻塞,最好也不要阻塞主线程。

编辑:

我刚刚意识到我正在使用Interval块来读取Heartbeat()中的while,所以我将其从while (Interval < 100)更改为while (counter < 100)

Edit2:

  1. 上面的代码是一个非常简化的版本,实际代码有点混乱,我认为发布所有API部分都不会解决这个问题。

  2. 不需要,只要更新了时间间隔,就不需要主线程来执行某些操作。每个循环更可能只读取一次,如下所示:

int interval;
while (Interval < 100 /* I could use some other condition not invoving Interval */ ) {
    interval = Interval;
    // all usage of "Interval" from this point would use the value read in interval
    Console.WriteLine($"Interval  : {interval}");
    Thread.Sleep(100);
}
  1. 只要在主线程中读取的值是合法值(在Heartbeat线程中计算),别无其他,我就可以了。例如,如果在我为该循环读取值时恰好在同一时间更新该值,则可以获取旧值或新值,只要它不是无处不在的无关紧要的值即可。 (如果它读取旧值,它将在下一个循环中读取新值,对吧?)

1 个答案:

答案 0 :(得分:0)

即使没有阻塞和很短的工作时间,您的计时器线程也会遭受滑移,因为它的工作时间不为0。我认为使用计时器会更好。

请查看Timer Class已在内置的多线程设置中运行。

  

基于服务器的System.Timers.Timer类设计为与多线程环境中的工作线程一起使用。服务器计时器可以在线程之间移动以处理引发的Elapsed事件,与Windows计时器相比,按时引发事件的准确性更高。

  

如果SynchronizingObject属性为null,则在ThreadPool线程上引发Elapsed事件。如果对Elapsed事件的处理持续时间长于Interval,则该事件可能在另一个ThreadPool线程上再次引发。在这种情况下,事件处理程序应该是可重入的。

最后:

  

提示

     

请注意,.NET包含四个名为Timer的类,每个类提供不同的功能:

     
      
  • System.Timers.Timer (本主题):定期触发事件。该类旨在在多线程环境中用作基于服务器的组件或服务组件。它没有用户界面,并且在运行时不可见。
  •   
  • System.Threading.Timer :定期在线程池线程上执行单个回调方法。回调方法是在实例化计时器且无法更改时定义的。与System.Timers.Timer类类似,该类旨在用作多线程环境中的基于服务器或服务的组件。它没有用户界面,并且在运行时不可见。
  •   
  • System.Windows.Forms.Timer (仅.NET Framework):一个Windows Forms组件,该组件定期触发事件。该组件没有用户界面,并且设计用于单线程环境。
  •   
  • System.Web.UI.Timer (仅.NET Framework):一个ASP.NET组件,该组件定期执行异步或同步网页回发。
  •