2016-03-06 111 views
1

我想了解TPL。不幸的是,我无法用返回类型来克服任务。从我读过的内容来看,我认为将任务分配给变量会异步启动。当您需要返回值时,您只需等待它,这可确保当前线程等待Task<T>完成。在MSDN为什么任务<TResult>在等待结果前等待结果?

例子:

// Call and await in separate statements. 
Task<int> integerTask = TaskOfT_MethodAsync(); 

// You can do other work that does not rely on integerTask before awaiting. 
textBox1.Text += String.Format("Application can continue working while the Task<T> runs. . . . \r\n"); 

int result2 = await integerTask; 

我的理解:第一条语句应该启动任务后,马上文本框被追加。然后线程被阻塞,直到integerTask完成。

然而,当我试图对我自己,它没有工作方式:

static void Main() 
{ 
    var task = new Task(RunItAsync); 
    task.Start(); 
    task.Wait(); 
} 
static async void RunItAsync() 
{ 
    // Should start the task, but should not block 
    var task = GetIntAsync(); 
    Console.WriteLine("I'm writing something while the task is running..."); 
    // Should wait for the running task to complete and then output the result 
    Console.WriteLine(await task); 
} 
static Random r = new Random(); 
static async Task<int> GetIntAsync() 
{ 
    return await Task.FromResult(GetIntSync()); 
} 
public static int GetIntSync() 
{ 
    // Some long operation to hold the task running 
    var count = 0; 
    for (var i = 0; i < 1000000000; i++) { 
     if (i % 2 == 0) count++; 
    } 
    return r.Next(count); 
} 

没有输出,几秒钟后它输出眼前的一幕:

I'm writing something while the task is running... 
143831542 

我做错了什么?

+1

我认为你需要退后一步,更深入了解,使异步工作所需的所有概念。 Stephen Cleary的一系列文章是对这个主题的一个很好的介绍。 http://blog.stephencleary.com/2012/02/async-and-await.html –

+0

是的,很遗憾,我错误地了解了我所学到的大部分内容。 – Sorashi

+0

这是棘手的东西。如果你想要更多的技术性的东西,虽然这个东西有点过时,但是当我们设计它的时候,我写了一系列文章。 https://blogs.msdn.microsoft.com/ericlippert/tag/async/。从底部开始,这些列表从大多数到最近都列出。 –

回答

10

ROM我读过,我认为赋值给一个变量任务异步启动它。

这是完全错误的。

在这里,我有一个任务:做一个三明治。我把它写在一张纸上,然后放在一个标有“任务”的抽屉里。我开始做三明治了吗?号码

我开始制作一个三明治。开始制作三明治是否会在我的抽屉中出现一张描述任务的纸张?号码

变量和任务完全没有关系。不管是什么原因让你相信他们之间有什么关系,现在不要再相信了。

当你需要返回值,你只是等待它

是。

确保当前线程等待任务完成。

不,如果通过“等待”您的意思是“块”。

我的理解:第一条语句应该启动任务

当然,假设TaskOfTMethodAsync启动任务并返回启动任务,这似乎是合理的。

立即后的文本框中附加

是。

然后线程被阻塞,直到integerTask完成。

完全不是。等待的关键是不要阻止线程!如果你给我一项任务 - 做一个三明治 - 并且你等着我完成这个任务,那么当我让你三明治的时候,你不要小憩了!你不断完成工作!否则,雇用我来让你成为三明治有什么意义?你想雇用工人为你做工作,而你做别的事情,而不是当你睡觉等待他们完成。

我在做什么错?

两件事情。首先,你将一个无效的方法变成一项任务,这是非常错误的。异步方法不应该是无效的,它应该返回一个任务!任务代表异步工作。其次,你只是返回完成的任务!

让我们一行一行地说一遍,说出它的作用。

var task = new Task(RunItAsync); 

创建一个任务来表示该操作。

task.Start(); 

在线程池中启动任务。

然后主线程阻塞任务的完成。

好吧,这种方法是在线程池运行:

static async void RunItAsync() 
{ 

这是什么电话呢?

var task = GetIntAsync(); 

好吧,让我们来看看:

static async Task<int> GetIntAsync() 
{ 
    return await Task.FromResult(GetIntSync()); 
} 

第一件事情就是打电话GetIntSync。这会持续很长时间。然后它返回一个值。然后我们产生一个代表该值的完成的任务。然后我们等待那个任务。等待已完成的任务会产生价值。所以这与

value1 = GetIntSync() 
    task1 = completed task representing value1 
    value2 = value of task1 
    task2 = completed task representing value2  
    return task2 

这里完全没有异步。这是编写“return GetIntSync()”的非常非常复杂的方法 - 运行该方法,然后构造不是一个,而是两个完成的任务,代表已完成的任务!

再次说明:此方法返回一个任务。这个方法的所有子任务已经完成了,所以我们只需返回一个完成的任务。

该任务会发生什么?请记住,我们在:

var task = GetIntAsync(); 

这样,任务就完成了。 (这是“任务2”。)

Console.WriteLine("I'm writing something while the task is running..."); 

不,你正在写任务后完成任务。你返回了一个完成的任务!

// Should wait for the running task to complete and then output the result 
Console.WriteLine(await task); 

不,任务已经完成。这会提取值而不等待并打印它。没有什么可以等待的;任务已经完成!

现在从这个方法返回什么?没有。它是一个无效的方法。所以我们现在回来了,完成了方法的所有工作。

接下来会发生什么?传递给原始任务的委托已在工作线程上完成运行,因此在工作线程上运行的任务完成并向主线程发出可以停止等待的信号。

0

在你的代码有

public static int GetIntSync() 

Getintsync有只是一个普通的无聊常规,你在写第一行之前调用它,所以它做的所有工作,然后说,即时通讯做别的事情。

的例子工作,因为调用的函数是一个异步功能,所以它的建立与

Task<T> something = myAsyncFunc(params) 
Now output im going to do something 
T result = await myAsyncFunc.. 

现在导致有结果做其他事情。

这就像建立一个deligate

第一行基本上是deligate,所以犯规动作代码。在等待的时候,你的代码在打我。

microsofts example

我登记,所以不是最好的地方,但我希望能看到你的错误,以及如何解决它

1

有几个问题。

  1. 你在这里没有开始任何异步。 async-await不会自动使事物异步或创建任何线程。阅读stephen's introductory article on the subject

    要卸载一些工作,您需要使用Task.RunTask.Factory.StartNew

  2. 您正在使用async void方法;没有简单的方法来等待它的完成。您应该使用async TaskRead more

所以,你的代码就变成了:

static Task<int> GetIntAsync() 
{ 
    return Task.Run(()=> GetIntSync()); 
} 

static async Task RunItAsync() 
{ 
    // Should start the task, but should not block 
    var task = GetIntAsync(); 
    Console.WriteLine("I'm writing something while the task is running..."); 
    // Should wait for the running task to complete and then output the result 
    Console.WriteLine(await task); 
} 

而且你不需要使用task.Start();手动启动该任务; Task.Run已经交给你一个开始的任务。

鉴于两个改变你的代码应该按预期工作

1

我在这里看到两个大问题: 首先是你使用Task.FromResult(result):它只不过是包装结果,所以它看起来像一个任务,所以你可以使用任务的方法。当你调用return await Task.FromResult(GetIntSync());,它:

  1. 计算GetIntSync完全
  2. 返回int
  3. 把它传递给FromResult
  4. 立即与成品Task<int>返回,
  5. 呼叫await对完成任务,所以它马上继续,
  6. 并返回Task<int>

GetIntAsync不同步了!第二大问题是你正在混合测试你的异步/等待技能(也使用Task),并行执行使用Task.Run。异步执行意味着线程的执行顺序不一定与代码顺序同步。尽可能快地执行异步方法中的代码。异步方法可以等待,控制权返回给调用者,直到它表示它已准备好再次工作。这样做的目的是保持线程以及CPU忙碌。为了说明,我们使用我最喜欢的机制:metaphores!

  • 让逻辑处理器成为桌面工作者。每次切片时,您都会走到一个桌子(线程)上,并在其上面放置一个文件夹(方法),一个接一个地处理其中的表格(线条)。如果一行是一种方法,您可以抓住该文件夹,处理里面的表格,然后返回到您正在处理的文件夹。

  • 然后一张表格X需要你老板的签名。你把它交给你的老板:从Worker.WorkDay()调用异步方法Task task = GetBossToSign(Form X)。然后你在其他表格上工作。当你拿回X签名时,你完成了X,然后继续你的位置,而不是浪费一秒钟。

  • 但是现在您来到表单Y,要求X已完成。幸运的是,Worker.WorkDay()被标记为async,因此您可以拨打await task来表示:在此文件夹下工作,直到您看到X还给您,仍在处理此过程。只有标记为async的文件夹可以通过await安全地收起。另一方面,如果您打电话给task.Wait(),那么您将该桌面标记为禁区(阻塞线程),直到任务完成。如果在使用底层文件夹时使用await,也会发生这种情况。工作人员(处理器)将尝试找到另一个工作台(准备好的线程),这可能与您试图精简的实际进程无关。

  • 如果你打电话给Task.Run(RunItAsync)(或者创建一个任务,然后开始它),你会去一些空的桌子,并在那里留下一个文件夹RunItAsync。你将在自己的桌子上工作。最理想的做法是快两倍。如果你是唯一的工作人员(处理器),那么它什么也不做,因为你只是要在两个办公桌上分配工作时间片。

  • 所以,现在你的代码做了什么:一个线程工作在办公桌A,其中Main是文件夹。它抓住文件夹RunItAsync,将其放在空桌子B上,然后将此桌面标记为不可用。 B(可能是也可能不是同一个工作人员)的工作人员呼叫GetIntAsync,然后GetIntSync。他自己填写表格,将完成的文件夹放在桌面C上,使用Task.FromResult,然后检查文件夹是否在桌面上。C完成await。它是!所以await什么都不做,GetIntAsync返回。完成Task分配给任务,B打印第一行,检查并看到task已完成,并打印第二行。文件夹RunItAsync已完成,因此办公桌A被标记为可用,办公桌B被放弃。一名工人(也可能是也可能不是同一名工人)前往办公桌A并执行task.Wait()之后的任何事情。

    • 因此,程序中没有两个工作人员同时工作,也没有任何执行与写入的代码异步执行。

要测试你的非同步/等待技能,只需使用一个非同步方法已经可用,例如WebClientDownloadStringTaskAsync("http://stackoverflow.com")。例如:

//Calling asynch method, running on the same thread, 
//doing the work as it is eligible for doing.; 
Task<string> x = new WebClient().DownloadStringTaskAsync("http://stackoverflow.com"); 
Console.WriteLine("I'm writing something while the task is running..."); 
Console.WriteLine("Content of webpage is: {0}", await x); 

此执行的线程传递控制到当前方法的调用者,直到下载完成后,通过使用await x

为了测试你的任务并行技能,任何同步工作都可以。

//Calling Task to delegate work to be done in parallel, 
//running(hopefully) on another thread. 
Task<int> longTask = Task.Run(() => {Thread.Sleep(1000); //this is hard work 
            return 1;}); 
Console.WriteLine("I'm writing something while the task is running..."); 
Console.WriteLine("The number one is: {0}", await longTask);