【问题标题】:C# Triggering event from new task [duplicate]C#从新任务触发事件[重复]
【发布时间】:2021-05-20 14:18:05
【问题描述】:

我想测试是否可以使用在新任务中工作的方法触发事件。

当我这样做时:

using System;
using System.Threading.Tasks;


namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {

            subscriber f = new subscriber();
        }

    }


    class subscriber
    {
        publisher x;
        public subscriber()
        {
            x = new publisher();
            x.ThresholdReached += c_ThresholdReached;
            x.method2();
        }

        static void c_ThresholdReached(object sender, EventArgs e)
        {
            Console.WriteLine("The threshold was reached.");
        }
    }



    class publisher
    {
        public event EventHandler ThresholdReached;


        public publisher()
        {
       
        }

        public void method1()
        {
            OnThresholdReached(EventArgs.Empty);
        }

        public void method2()
        {
            Task.Run(() => method1());

        }


        protected virtual void OnThresholdReached(EventArgs e)
        {
            EventHandler handler = ThresholdReached;
            handler?.Invoke(this, e);
        }

    }
}

输出什么都没有!

但是,当我这样做时:

using System;
using System.Threading.Tasks;


namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {

            subscriber f = new subscriber();
        }

    }


    class subscriber
    {
        publisher x;
        public subscriber()
        {
            x = new publisher();
            x.ThresholdReached += c_ThresholdReached;
            x.method2();
        }

        static void c_ThresholdReached(object sender, EventArgs e)
        {
            Console.WriteLine("The threshold was reached.");
        }
    }



    class publisher
    {
        public event EventHandler ThresholdReached;


        public publisher()
        {
       
        }

        public void method1()
        {
            OnThresholdReached(EventArgs.Empty);
        }

        public void method2()
        {
            //Here is the change
            method1();
            Task.Run(() => method1());

        }


        protected virtual void OnThresholdReached(EventArgs e)
        {
            EventHandler handler = ThresholdReached;
            handler?.Invoke(this, e);
        }

    }
}

输出是这样的:

The threshold was reached
The threshold was reached

这很奇怪! 我不明白为什么它会打印两次。

但是,我排除了它不适用于 method1 在新任务中运行,因为它不会在同一个线程中工作

有人能解释一下为什么吗? 有没有办法与父线程通信并发方法?

提前致谢

【问题讨论】:

  • 第一个代码不打印任何内容,因为Main 方法在method1 执行之前结束,这使得控制台应用程序在打印消息之前关闭。
  • Main 中添加Console.ReadKey(); 让控制台等待输入。

标签: c# asynchronous task


【解决方案1】:

正如评论中提到的,您的代码在任务运行之前完成并启动任何内容。您应该以异步方式修改代码以等待任务完成

using System;
using System.Threading.Tasks;


namespace ConsoleApp2
{
    class Program
    {
        static async Task Main(string[] args)
        {
            subscriber f = new subscriber();
            await f.Test();
        }

    }


    class subscriber
    {
        publisher x;
        public subscriber()
        {
            x = new publisher();
            x.ThresholdReached += c_ThresholdReached;
        }
        
        public async Task Test()
        {
            await x.method2();
        }

        static void c_ThresholdReached(object sender, EventArgs e)
        {
            Console.WriteLine("The threshold was reached.");
        }
    }


    class publisher
    {
        public event EventHandler ThresholdReached;


        public publisher()
        {
       
        }

        public void method1()
        {
            OnThresholdReached(EventArgs.Empty);
        }

        public async Task method2()
        {
            await Task.Run(() => method1());
        }


        protected virtual void OnThresholdReached(EventArgs e)
        {
            EventHandler handler = ThresholdReached;
            handler?.Invoke(this, e);
        }

    }
}

至于您的第二个 sn-p,看起来任务有时间运行和 pring,而主线程在 method1 内打印。有时它会打印两次(如果有足够的时间)或打印一次(如果主线程中的 Console.Write 将在任务运行之前完成)。

附加说明:请注意,您的事件处理程序不会在主线程中调用,而是在执行任务的线程中调用(通常,这将是来自线程池的线程)

【讨论】:

    【解决方案2】:

    当你调用Task.Run(...)时,Run()里面的动作会在新线程中启动。

    在第一种情况下,您使用Task.Run() 启动新线程,但程序在执行新线程之前完成并退出。 您可以通过在Main() 的末尾添加Console.Read() 来防止主线程终止。这样您就可以等待第二个线程完成。

    class Program
    {
        static void Main(string[] args)
        {
            subscriber f = new subscriber();
            Console.Read();
        }
    
    }
    

    在第二种情况下,您将收到两条消息:

    public void method2()
    {
        method1();
        Task.Run(() => method1());
    }
    

    这一次,由于您直接调用method1(),您肯定会在控制台中写入一次消息。由于在控制台上打印(一般任何 IO)都比较慢,所以第二个线程(由Task.Run() 创建)将有时间执行,并第二次打印消息。

    另一种解决方案是手动创建线程

    public void method2()
    {
        Thread t1 = new Thread(() => method1());
        t1.IsBackground = false;
        t1.Start();
    }
    

    在所有前台线程都完成之前,程序不会退出。所以当你设置IsBackground = false;时,程序会等待那个线程

    【讨论】:

      猜你喜欢
      • 2019-03-15
      • 1970-01-01
      • 2013-05-26
      • 2020-09-11
      • 1970-01-01
      • 2021-05-06
      • 2018-11-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多