【问题标题】:Service isn't running Task.run服务未运行 Task.run
【发布时间】:2020-02-09 05:33:50
【问题描述】:

我写了一个 Windows 服务。 下面是类内部定义的全局变量:

private static TimeSpan pollingInterval;
private static Thread regenerationThread;
private static ManualResetEvent quitThreadEvent;
private static ConcurrentQueue<KeyValuePair<string, string>> queue = new ConcurrentQueue<KeyValuePair<string, string>>();

这里是服务的启动方法:

internal static void Start()
{
    pollingInterval = TimeSpan.FromSeconds(100);
    quitThreadEvent = new ManualResetEvent(false);
    regenerationThread = new Thread(doStart);
    regenerationThread.Start();
}

这里是上面提到的doStart方法:

internal static void doStart()
{   
    queue.Enqueue(new KeyValuePair<string, string>("user1", "password1"));
    queue.Enqueue(new KeyValuePair<string, string>("user2", "password2"));
    queue.Enqueue(new KeyValuePair<string, string>("user3", "password3"));
    while (!quitThreadEvent.WaitOne(pollingInterval))
    {
        // tempList isn't empty, I checked it putting logs here.
        if (tempList.Length != 0)
        {
            var temp = new KeyValuePair<string, string>();
            if (queue.TryDequeue(out temp))
            {
                string username = temp.Key;
                string password = temp.Value;
                // Method comes till here but doesn't execute the statement below and directly reach to end WriteToFile method.
                Task.Run(async () =>
                {
                    await tempFunc(username, password);
                    queue.Enqueue(new KeyValuePair<string, string>(username, password));
                });
            }
        }
        WriteToFile("End of Method");
    }
}

我不确定为什么 Task.Run 不起作用。我已经放了日志,但它没有显示任何错误。我尝试将日志放在Task.run 中,但它没有显示这些日志。是我开始一个线程然后在里面调用Task.run的原因吗?

【问题讨论】:

  • 你为什么不用Task.Run() 而不是线程?
  • 在这种情况下根本不需要Task.Run
  • 我没听懂你。您是否建议而不是 regenerationThread = new Thread(doStart);regenerationThread.Start(); 声明。使用Task.Run() 启动方法doStart ? @克林特
  • 您的DoStart() 可以是async Task DoTask()
  • @SirRufo,实际上我希望tempFunc 方法被异步调用多次,所以我认为Task.Run 是这样做的正确方法吗?还是我在这里遗漏了什么?

标签: c# multithreading windows-services


【解决方案1】:

我已将您的代码重写为仅使用 async/await 模式。

private static TimeSpan pollingInterval;
private static Task regenerationTask;
private static CancellationTokenSource quitThreadEvent;
private static ConcurrentQueue<KeyValuePair<string, string>> queue = new ConcurrentQueue<KeyValuePair<string, string>>();

internal static void Start()
{
    pollingInterval = TimeSpan.FromSeconds(100);
    quitThreadEvent = new CancellationTokenSource();
    regenerationTask = ExecuteAsync( quitThreadEvent.Token );
}
internal static async Task ExecuteAsync(CancellationToken cancellationToken)
{   
    queue.Enqueue(new KeyValuePair<string, string>("user1", "password1"));
    queue.Enqueue(new KeyValuePair<string, string>("user2", "password2"));
    queue.Enqueue(new KeyValuePair<string, string>("user3", "password3"));
    while ( true )
    {
        await Task.Delay( pollingInterval, cancellationToken ).ConfigureAwait( false );
        // tempList isn't empty, I checked it putting logs here.
        if (tempList.Length != 0)
        {
            var temp = new KeyValuePair<string, string>();
            if (queue.TryDequeue(out temp))
            {
                string username = temp.Key;
                string password = temp.Value;

                // fire and forget task - we do not want to await
                // so we do not need to store the task instance
                _ = WorkWithAsync(username, password);
            }
        }
        WriteToFile("End of Method");
    }
}

private static async Task WorkWithAsync( string username, string password )
{
    await tempfunc( username, password ).ConfigureAwait( false );
    queue.Enqueue(new KeyValuePair<string, string>(username, password));
}

如果你错过了Stop 方法

internal static void Stop()
{
    quitThreadEvent.Cancel();
    regenerationTask.Wait();
}

internal static async Task StopAsync()
{
    quitThreadEvent.Cancel();
    await regenerationTask;
}

【讨论】:

  • await Task.Delay( pollingInterval, cancellationToken ).ConfigureAwait( false ); 这到底是做什么的?假设我有一个async 方法,因为我必须在其中输入await,但我不想等待其他任何东西。因此,如果我在上面的语句中将pollingInterval 设置为 0,那会完成这项工作吗?试图理解我提到的陈述。
  • 实际上是在谈论tempFunc,如果我在tempFunc 中有一堆语句,而我只是不想在任何地方等待并异步执行该方法,则使await Task.Dealy(0) 会这样做给我?
  • @sporty_1993 Task.Delay( ... ) 将等待给定的延迟,但当 cacellationToken 处于取消状态时会引发异常。这有点像你的 ManualRestEvent。 ConfigureAwait( false ) 将导致任务捕获当前 SynchronizationContext 并将在工作线程上继续。
  • Task.Delay( 0 ).ConfigureAwait( false ) 和你什么都不写一样。如果你想继续一个工作线程,你必须await Task.Delay( 1 ).ConfigureAwait( false );
  • 在你提到的代码中,所以代码执行将在await Task.Deay() 处停止pollingInterval,然后继续进行,对吧?但在这里await tempFunc(username, password).ConfigureAwait( false ); 它将在主线程中继续运行并同时运行tempFunc
【解决方案2】:

尽管您的代码不像人们所说的那样是一种好的实现方式,但您的代码也应该可以工作,除非您的代码在其他地方出现问题。我在本地尝试过,它有效。但是 'while (!quitThreadEvent.WaitOne(pollingInterval))' 会阻止内部代码执行 'pollingInterval'(100 秒),除非您在其他地方设置了 quitThreadEvent。确保您的程序不会在 100 秒之前结束。如果提前结束,您可能看不到执行结果。 这是一个工作版本代码:

    internal static void Start()
    {
        pollingInterval = TimeSpan.FromSeconds(1);
        quitThreadEvent = new ManualResetEvent(false);
        regenerationThread = new Thread(doStart);
        regenerationThread.Start();
    }

    internal static void doStart()
    {
        queue.Enqueue(new KeyValuePair<string, string>("user1", "password1"));
        queue.Enqueue(new KeyValuePair<string, string>("user2", "password2"));
        queue.Enqueue(new KeyValuePair<string, string>("user3", "password3"));
        while (!quitThreadEvent.WaitOne(pollingInterval))
        {
            // tempList isn't empty, I checked it putting logs here.
            if (true)
            {
                var temp = new KeyValuePair<string, string>();
                if (queue.TryDequeue(out temp))
                {
                    string username = temp.Key;
                    string password = temp.Value;
                    // Method comes till here but doesn't execute the statement below and directly reach to end WriteToFile method.
                    Task.Run(async () =>
                    {
                        await tempFunc(username, password);
                        queue.Enqueue(new KeyValuePair<string, string>(username, password));
                    });
                }
                Console.WriteLine("End of the method.");
                break;
            }
        }
    }

    internal static async Task tempFunc(string userName, string pwd)
    {
        Console.WriteLine($"UserName = {userName}, Pwd = {pwd}");
        await Task.Delay(1000);
    }

    static void Main(string[] args)
    {
        Start();
        Thread.Sleep(5000);
    }

`

【讨论】:

  • 我只在停止服务时设置quitThreadEvent。但是该方法仍然没有被调用,我不知道为什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-24
相关资源
最近更新 更多