阻塞方法在任务中的应用

4

我目前正在开发一个小型服务器应用程序,并学习任务<>和其他相关操作。

我想知道在任务中如何使用阻塞操作。

例如,我目前使用了几个具有“阻塞”操作的库。其中之一是Npgsql(PostgreSQL提供程序)。

如果我执行以下操作...

Task myTask = new Task<>( () => 
    {
      using(var db = new PostgresqlDatabaseConnection())
      {
             db.ExecuteQuery("SELECT takes 50 ms to get data...")
             db.Insert(anObject); etc....
      }

   }
).Start();

请将其与处理该数据的其他任务链接在一起。

这样有效吗? 比如说,ExecuteQuery调用某种Thread.Sleep(1)或以某种方式阻塞线程,这会影响我的任务执行吗?

我问这个问题是因为我的服务器使用了几个库,必须重新编写以适应完全异步的方法。 或者这已经足够异步了吗?

*我的想法 *

我真的不确定。

我知道,例如,如果db.Executre()只运行while(true)循环直到获取数据,它几乎肯定会阻塞我的服务器。因为大量时间将花费在while(true)上进行处理。或者Task是否足够聪明,知道它应该在此花费更少的时间?或者如果它在内部使用某种等待机制,Task库是否知道?它是否知道它应该在等待时处理其他任务。


当你说“将它链接到一堆其他任务”时,你是指使用ContinueWith将它们链接在一起吗?如果是这样,那么第二个任务直到第一个任务完成后才会执行。重新阅读后,我猜你是指ContinueWith,因为你说后续任务依赖于那些数据。此外,就效率而言,如果任务2需要任务1的数据,任务3需要任务2,则即使第一个任务休眠,你也必须按顺序执行它们。除非有独立运行的部分,否则不要认为你可以获得任何效率提升。 - pstrjds
我的示例的一般流程是:接收请求->获取数据库数据->执行一些业务逻辑->发送回请求/或其他功能。我更关心同时运行20个获取数据库数据任务并阻塞其他服务器功能的执行。ContinueWith将用于链接下一个任务,但这与获取数据库信息所需的时间相差甚远。 - user3431277
3个回答

4
我正在开发一个小型服务器应用程序,并且正在熟悉Task<>和其他相关操作。在服务器端应用程序中,除非并发客户端连接数真的很低,否则使用new TaskTask.Factory.StartNewTask.Run是没有好处的。请查看此文此文以获取更多细节。
然而,您可以从使用自然异步API中受益匪浅。它们不会在“飞行”期间阻塞池线程,因此线程将返回到池中,然后可以忙于为另一个客户请求提供服务。这提高了您的服务器应用程序的可扩展性。
我不确定PostgreSQL是否提供这样的API,请寻找类似于ExecuteQueryAsyncBeginExecuteQuery/EndExecuteQuery的内容。如果没有,请只使用同步的ExecuteQuery方法,但不要像代码片段中那样将其转移到池线程上。

@user3431277,显然你选择使用Task包装器将同步代码转移到后台线程中,在你的服务器端应用程序中。这是一个众所周知但仍然流行的反模式。请查看此链接:http://blog.stephencleary.com/2013/10/taskrun-etiquette-and-proper-usage.html - noseratio - open to work

3
使用C# 5的async/await特性绝对会让事情变得更加容易。它可以使异步代码更易于编写,因为你可以像编写同步代码一样编写它。
以以下示例为例。我正在使用Thread.Sleep来模拟一个长时间运行的操作,因此任何不支持异步的库都可以通过Task.Run来使用。而在Thread.Sleep阻塞线程时,您的UI仍然响应。如果您以同步方式编写此代码,则您的UI将等待1.5秒,直到thread.sleep完成。
private async void button1_Click(object sender, EventArgs e)
{
    Console.WriteLine("0");
    await DoWorkAsync();
    Console.WriteLine("3");
}

private async Task DoWorkAsync()
{
    Console.WriteLine("1");
    await Task.Run(()=>
        {
            // Do your db work here.
            Thread.Sleep(1500);

        });
    Console.WriteLine("2");
}

简而言之,如果您有一个长时间运行的数据库操作,并且希望保持UI响应性,则应利用async / await。虽然这确实可以保持UI的响应性,但它也会引入新的挑战,例如:多次单击按钮时会发生什么,或者在您仍在处理时用户关闭窗口等简单情况。我鼓励您进一步阅读此主题。Jon Skeet有一个关于async的多部分系列。还有许多MSDN文章涉及此主题:1 2 3 ...

1

异步编程对你的逻辑或数据库的效率没有任何改进作用,它所影响的只是操作之间的切换性能。

通过将查询或计算包装在Task中,你无法使其更快。你只会增加额外开销。

服务器上使用异步IO可以实现扩展到数百个并发请求的可伸缩性。但这里你不需要它。


事实上,确实如此!我会转向它们,但是我还有其他几个库不支持。你的回答帮助我在脑海中巩固了这个概念。 - user3431277

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