【问题标题】:Async seems to be synchronous异步似乎是同步的
【发布时间】:2019-12-09 08:37:33
【问题描述】:

我不确定我是否在这里遗漏了什么,但更多的 for 循环似乎正在同步执行,即使我等待它之外的所有任务。

下面是我的代码:

static void Main(string[] args) {
    var t = Start();
}

public static async Task < List < Task < TaskInfo >>> Start() {
    var listOfTasks = new List < Task < TaskInfo >> ();
    for (var i = 0; i <= 100; i++) {
        var process = new Processor();
        listOfTasks.Add(process.Process(i));
    }

    await Task.WhenAll(listOfTasks);
    return listOfTasks;
}

我传入 taskId 以注销只是为了查看任务执行的顺序。

我在这里遗漏了一些非常明显的东西吗?

编辑:

根据下面的答案和 cmets 将代码更改为此,它仍然同步显示:

    public class StartWork
{
    public int TaskId { get; set; }
    public Processor Processor { get;}

    public StartWork()
    {
        Processor = new Processor();
    }
}

    static void Main(string[] args)
    {
        var t = Start();
    }

    public static async Task<TaskInfo[]> Start()
    {
        var tasks = new List<StartWork>();

        for (int i = 1; i < 100; i++)
        {
            var work = new StartWork
            {
                TaskId = i
            };
            tasks.Add(work);
        }

        return await Task.WhenAll(tasks.Select(i => i.Processor.Process(i.TaskId)));
    }

我在处理器类中调用的函数:

 public Task<TaskInfo> Process(int taskId)
    {
        try
        {
            taskId = taskId + 1;
            stopwatch.Start();
            using (var bus = RabbitHutch.CreateBus(xxDev))
            {
                @event = new AutoResetEvent(false);

                var replyTo = Guid.NewGuid().ToString();
                var messageQueue = bus.Advanced.QueueDeclare(replyTo, autoDelete: true);

                bus.Advanced.Consume(messageQueue, (payload, properties, info) =>
                {
                    ReceivePdf(payload, properties, info);
                    return Task.FromResult(0);
                });

                taskInfo.InputFile = inputFile;
                var html = File.ReadAllText(inputFile);
                taskInfo.Html = html;

                var message = PrepareMessage(new RenderRequest()
                {
                    Html = Encoding.UTF8.GetBytes(html),
                    Options = new RenderRequestOptions()
                    {
                        PageSize = "A4",
                        ImageQuality = 70,
                        PageLoadRetryAttempts = 3
                    }
                });

                var correlation = Guid.NewGuid().ToString();
                Console.WriteLine($"CorrelationId: {correlation}, TaskId {taskId}");

                var props = new MessageProperties
                {
                    CorrelationId = correlation,
                    ReplyTo = replyTo,
                    Expiration = "6000"
                };

                Publish(bus, props, message);
                taskInfo.CorrelationId = Guid.Parse(correlation);
                @event.WaitOne();
                stopwatch.Stop();
                taskInfo.TimeTaken = stopwatch.Elapsed;
                return Task.FromResult(taskInfo);
            }
        }
        catch (Exception e)
        {
            taskInfo.OutputFile = Empty;
            return Task.FromResult(taskInfo);
        }
    }

        void ReceivePdf(byte[] payload, MessageProperties properties, MessageReceivedInfo info)
    {
        var file = Format(outputFile, properties.CorrelationId);
        taskInfo.OutputFile = file;

        Console.WriteLine("Output written to " + file);

        File.WriteAllBytes(file, payload);

        var remaining = Interlocked.Decrement(ref outstandingRequests);

        if (remaining == 0)
        {
            @event.Set();
        }
    }

【问题讨论】:

  • 循环同步的。循环添加到列表中的任务是异步的。
  • 你必须 await Start 并把 Main 设为 async
  • 请解释为什么你认为它是同步的,你观察到的与你预期的不同(当然,还要解释你预期会发生什么)。你也可以在process.Process中显示代码吗?如果该代码本质上不是异步的,那么您问题中的代码的作用并不重要。
  • @LasseV.Karlsen - 在控制台窗口中,它按顺序输出 taskId,例如 1,2,3,4,5,6 我希望顺序会有所不同,如果它是异步运行?
  • Process 方法中没有异步代码,这就是原因。即使您添加了任务和 async 关键字,一切仍然是同步的。

标签: c# asynchronous async-await


【解决方案1】:

这是一个同步任务

listOfTasks.Add(process.Process(i));

您只是将项目添加到列表中。

这也是一个同步任务

process.Process(i);

上面的函数返回的任务是异步的,它会在你代码的whenAll调用中异步执行。

请记住,当 all 将等待所有任务运行时,如果任务是微不足道的,因为它们一个接一个地启动,大多数时候会偶然按顺序运行。

如果执行的任务代码根据输入在执行时间上有所不同,您会看到一些差异。

【讨论】:

  • 不要粘贴到这里。编辑您的问题。 @TheKeyboarder
【解决方案2】:

首先异步并不意味着多线程,异步用于在不阻塞 UI 的情况下运行后台任务或在不阻塞主线程的情况下运行 I/O 操作。 通常操作系统使用多线程处理异步,但不能保证。 如果您想确保启动多个线程,请使用 Thread.Start。

无论如何,在您的代码中,您都强制您的代码同步运行,因为您在 Main 方法中调用异步方法 start 而没有等待。

您需要将代码更改为:

static async void Main(string[] args)
{
   var t = await Start();
}

或不等待(但程序有在任务完成之前终止的风险):

static void Main(string[] args)
{
   Task.Run(async () => {
       var t = await Start();
   });
}

【讨论】:

  • 实际上,如果不等待 Main 中的任务,他就有可能在所有任务完成之前终止进程。它不会改变被调用代码的异步性质。如果该代码真的是异步的,那么无论 Main 的结构如何,它仍然是异步的。他应该根据您的建议修复代码,但这并不能解释为什么他的其余代码是同步的。
猜你喜欢
  • 1970-01-01
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多