【问题标题】:About Threads Being Used in the Event Calling in C#关于 C# 事件调用中使用的线程
【发布时间】:2016-07-12 18:31:20
【问题描述】:

下面是我学习C#事件调用的代码,以及其中使用线程的情况。我在使用时有几个问题:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace EventSample1
{
    public class MyTimerClass
    {
        public event EventHandler MyElapsed;
        //----
        private void OnOneSecond(object source, EventArgs args)
        {
            if (MyElapsed != null)
            {
                MyElapsed(source, args);
            }
        }
        //----
        private System.Timers.Timer MyPrivateTimer; 
        //----
        public MyTimerClass()
        {
            MyPrivateTimer = new System.Timers.Timer();
            MyPrivateTimer.Elapsed += OnOneSecond;
            //----
            MyPrivateTimer.Interval = 1000;             
            MyPrivateTimer.Enabled = true;
        }
     }
    //----
    class classA
    {
        public void TimerHandlerA(object source, EventArgs args)
        {
            Console.WriteLine("{0} class A handler called! at thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
        }
    }
    class classB
    {
        public static void TimerHandlerB(object source, EventArgs args)
        {
            Console.WriteLine("{0} class B handler called! at thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
        }
    }
    //----
    class Program
    {
        static void Main()
        {
            Console.WriteLine("Main thread on {0}", Thread.CurrentThread.ManagedThreadId);
            classA ca = new classA();
            MyTimerClass mc = new MyTimerClass();
            //----
            mc.MyElapsed += ca.TimerHandlerA;
            mc.MyElapsed += classB.TimerHandlerB;
            //----
            Thread.Sleep(2250);
        }
    }
}

结果如下:

说明当mc.Elapsed被调用时,主线程开启另一个(invoke?)线程来执行两个EventHandler(TimerEventHandlerA和B)。

我的问题 1. 是:

为什么主线程标为1号,而新调用的线程标为4号? 那么,什么是2号线和3号线呢? 他们是做什么的?


然后我修改了main()中的代码,让主线程从2.25s'休眠到5.25s',

Thread.Sleep(5250);

我得到了不同的结果(另一个线程被调用):

我的问题 2。 是:

正在使用的线程数是否完全由系统决定?是人们称之为“线程池”的东西吗?


之后,我在Thread.Sleep() 之前插入另一行,看看会发生什么?

//----
mc.MyElapsed += ca.TimerHandlerA;
mc.MyElapsed += classB.TimerHandlerB;
Console.ReadLine();
//----
Thread.Sleep(2250);

我这样做的原因是:

据我了解,当事件 myElapsed 被调用时,A 和 B 将被调用。在调用它们之前,程序会使用一个新线程来执行TimerEventHandlerA和B中的代码。主线程很快就会回到readline()这一行,等待我们输入。就像我们使用Thread.Sleep()时一样,主线程刚刚回到Thread.Sleep(),并在2.25s'后关闭程序运行!

但结果不是像我们想的那样,TimerEventHandler A 和 B 只是被一次又一次地调用......就像主线程永远不会来到readline()

我只是在同一个位置使用不同的线,为什么结果如此不同?

我认为这可能是指线程中的一些细节,但是我的书并没有过多地谈论线程在C#中的使用,有人可以推荐另一本自学的好书吗?

提前致谢!

【问题讨论】:

  • Read msdn.microsoft.com/de-de/library/… - 事件被调用的线程上下文取决于定时器本身。你只能依赖:a)它会被调用 b)它不会在主线程上下文中被调用
  • 如果您将这些问题作为三个单独的问题重新发布,您可能会得到这些问题的答案。现在它有点太宽泛了。
  • @Enigmativity 感谢您的建议!可能你可以先给出一两个你觉得舒服的答案吗?谢谢!
  • @SirRufo 感谢您的回复!但首先,你能告诉我你回答哪个问题吗?第一个?第二个?还是第三个?

标签: c# multithreading


【解决方案1】:

为什么主线程标为1号,而新调用的线程标为4号?那么,什么是2号线和3号线呢?他们是做什么的?

不关心线程ID。

  • 每个线程都有一个唯一的 ID
  • System.Timers.Timer 会在线程内部调用定时器事件(可以是除主线程之外的任何线程)
  • System.Timers.Timer 可以使用尽可能多的线程来完成其工作

所以我们只能依赖事件将被调用,并且调用线程将具有与主线程不同的 id。

正在使用的线程数是否完全由系统决定?是人们称之为“线程池”的东西吗?

是的,如何调用定时器事件取决于System.Timers.Timer 的实现。我们真的不知道是否涉及线程池,但这真的很重要吗?只要实例的行为符合记录/预期,我就不关心实现。

如果我确实关心实现,我只需查看 sources 以了解幕后发生的事情。

...好像主线程永远不会来Console.ReadLine()

嗯,主线程肯定会到达那条线。但是Console.ReadLine() 到底是做什么的呢?

它正在等待来自该用户的输入,并在用户按下 ENTER 时继续。所以在你按下 ENTER 之前,主线程 被阻塞。但所有其他线程仍在运行,并将调用将一些文本输出到控制台的事件。

为了证明这一点,我们只需添加几行:

mc.MyElapsed += ca.TimerHandlerA;
mc.MyElapsed += classB.TimerHandlerB;

Console.WriteLine( "I am waiting for your input!" );
Console.ReadLine();
Console.WriteLine( "Thank you!" );

Thread.Sleep(2250);

Console.WriteLine( "Finished." );
主线程 1 我在等你的意见! 2016-7-12 7:53:40 A 类处理程序调用!在线程 4 2016-7-12 7:53:40 B 类处理程序调用!在线程 4 2016-7-12 7:53:41 A 类处理程序调用!在线程 4 2016-7-12 7:53:41 B 类处理程序调用!在线程 4

它会一直持续到您按下 ENTER 键。当您点击时,您应该会看到在 Thread.Sleep(2250) 期间仍会调用事件。

2016-7-12 7:53:50 A 类处理程序调用!在线程 4 2016-7-12 7:53:50 B 类处理程序调用!在线程 4 谢谢! 2016-7-12 7:53:51 A 类处理程序调用!在线程 4 2016-7-12 7:53:51 B 类处理程序调用!在线程 4 2016-7-12 7:53:52 A 类处理程序调用!在线程 4 2016-7-12 7:53:52 B 类处理程序调用!在线程 4 完成的。

谁能推荐另一本自学的好书?

不,这样的问题是off topic 在这里。

【讨论】:

  • 真的,真的谢谢你! @sir-rufo 你的回答很有条理,很清楚,尤其是最后一个问题……Console.ReadLine 的详细信息对于解决我的问题非常重要。但是你在哪里找到这些?它们是否也来自 msdn 上的链接?(如您在上面提供的链接?)
  • Console.ReadLine - 如果您不确定在哪里搜索,只需向 google 询问 .net Console.ReadLine,第一个链接应该是 msdn 链接
【解决方案2】:

我同意其他 cmets,这个问题太宽泛而无法涵盖是一个简洁的答案。我建议您查看此链接:http://www.albahari.com/threading/ 它涵盖了很多细节,并且很可能会为您提供这个以及许多其他问题的答案。

总的来说,我认为您过于担心幕后发生的事情的确切细节。是的,很高兴了解它们是如何联系在一起的,但是对于分配给线程 ID 的实际值,这并不重要。系统将为您管理线程分配,并且由于每个线程的执行顺序和运行时间是不确定的,您将无法从 ThreadID 本身的值推断出任何执行顺序。尽管我相信 threadID=1 始终是主线程 - 我个人不会依赖于将来总是如此。 当您更深入地了解线程时,将会发现使用锁定、锁存和信号量同步两个或多个线程的更重要方面。这是控制和/或跟踪执行顺序的正确方法,对于这些,知道 ThreadID 并不重要。

【讨论】:

  • 谢谢@phil-blackburn,这个链接特别有用!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-26
  • 1970-01-01
相关资源
最近更新 更多