如何最好地将同步代码封装成异步方法?

12

我正在使用async-await方法创建一个应用程序。但是使用它们对我来说存在一个大问题。在阅读了几篇文章后,我仍然不知道将我的重型同步操作包装成异步方法的最佳方式是什么。

我有两个想法。哪一个是最好的?

1)当前实现。

private Task<List<UploadedTestModel>> ParseTestFiles(List<string> filesContent)
{
    var tcs = new TaskCompletionSource<List<UploadedTestModel>>();
    Task.Run(() =>
    {
        var resultList = new List<UploadedTestModel>();
        foreach (var testBody in filesContent)
        {
            try
            {
                var currentCulture = Thread.CurrentThread.CurrentCulture;
                var serializerSettings = new JsonSerializerSettings
                {
                    Culture = currentCulture
                };

                var parsedData = JsonConvert.DeserializeObject<UploadedTestModel>(testBody, serializerSettings);
                resultList.Add(parsedData);
            }
            catch(Exception exception)
            {
                tcs.SetException(exception);
            }
        }
        tcs.SetResult(resultList);
    });
    return tcs.Task;
}

我正在使用 Task.Run 和 TaskCompletionSource。

2)仅使用 Task.Run,而不使用 TaskCompletionSource。

private Task<List<UploadedTestModel>> ParseTestFiles(List<string> filesContent)
{
    return Task.Run(() =>
    {
       . . . .
       return resultList;          
    });
}

Task.Run是你能得到的最好的选择 - 通过特殊的方式调用它并不能使本质上同步的操作真正变为异步。如果操作是CPU密集型的(甚至只是包装了一个阻塞线程的操作),它需要在一个线程上运行。 - Ant P
@KavehShahbazian 加载文件和反序列化正在阻塞我的UI线程。因此,我正在寻找解决这种情况的方法。 - Egorikas
1
@Egorikas 请记住,在 UI 应用程序中,asyncawait 的行为有些不同 - 它们保留上下文。因此,您必须使用显式的 Task 初始化;然后才能 awaitTask - Kaveh Shahbazian
顺便说一句,我在代码中没有看到 asyncawait。是我的眼睛自作主张加上了它们! - Kaveh Shahbazian
1
@KavehShahbazian 我明白了。谢谢你的解释。 - Egorikas
显示剩余4条评论
1个回答

24

我不会使用任何一种方式。这样做会欺骗调用方法的人。当你暴露一个异步操作时,调用者期望它是自然异步的,也就是说没有线程在后台执行操作。

所有你的方法都是同步的,应该按照这样的方式公开。让调用者决定是同步调用还是使用线程并将其排队,不要代替他们做决定。

有一篇非常棒的文章叫做“Should I expose asynchronous wrappers for synchronous methods?”,由Stephan Toub撰写,阐述了不采取你所尝试的做法的所有原因。建议阅读。


1
Lucian Wischik有一个视频,标题为异步库方法不应该欺骗,内容与此相同。 - Dour High Arch
1
@Yuval,我已经阅读了这篇文章。在阅读之后,我开始思考我的代码。所以,您能否解释一下?您是否提供将此方法同步化,然后使用任务调用它(它会阻塞我的UI线程,因此我需要做些什么)? - Egorikas
在像WPF、Windows Forms、UWP等应用程序中使用同步操作甚至有意义吗?您会阻塞UI线程并冻结应用程序。在这种情况下,将其包装为异步操作不是更好吗? - Emil
1
@batmaci 阻塞用户界面线程毫无意义。但我仍不会将方法呈现为异步的,我只会从事件处理程序中调用 Task.Run - Yuval Itzchakov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接