我试图了解TPL。不幸的是,我无法通过返回类型克服任务。根据我的阅读,我认为将任务分配给变量会异步启动它。当您需要返回值时,只需等待它,这可确保当前线程等待Task<T>
完成。
MSDN上的示例:
// 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
我做错了什么?
答案 0 :(得分:10)
我读过的内容,我认为将任务分配给变量会异步启动它。
这是完全错误的。
在这里,我有一个任务:做一个三明治。我把它写在一张纸上,然后把它放在标有&#34;任务&#34;的抽屉里。我开始做三明治吗?否。
我开始做三明治。开始制作三明治会导致一张纸上有任务描述出现在我的抽屉里吗?否。
变量和任务完全没有任何关系。无论是什么让你相信他们彼此有关,现在就不要相信了。
当您需要返回值时,您只需等待它
是
确保当前线程等待任务完成。
不,如果通过&#34;等待&#34;你的意思是&#34;阻止&#34;。
我的理解:第一个陈述应该开始任务
当然,假设TaskOfTMethodAsync启动任务并返回已启动的任务,这似乎是合理的。
之后立即附加文本框
是
然后线程被阻塞,直到integerTask完成。
绝对没有。等待的重点是不阻止线程!如果你给我一个任务 - 做一个三明治 - 然后你等着我完成那项任务,你不要小睡,而我却让你成为一个三明治!你一直在完成工作!另外,雇我让你做三明治是什么意思?你想雇佣工人为你做的工作,而不是在你等待他们完成的时候。
我做错了什么?
两件事。首先,你将void方法变成了一个任务,这是非常错误的。异步方法不应该是void,它应该返回一个任务!任务表示异步工作。其次,你只需要完成已完成的任务!
让我们一行一行地说出它的作用。
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
这里根本就没有异步。这是一种非常非常复杂的写入方式&#34;返回GetIntSync()&#34; - 运行该方法,然后构建不是一个,而是构建已完成任务的两个已完成的任务!
再次:此方法返回一个任务。此方法的所有子任务已经完成,因此我们只返回一个带有值的已完成任务。
这项任务会怎样?记得我们在:
var task = GetIntAsync();
这样任务就完成了。 (这是&#34; task2&#34;。)
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);
不,任务已经完成。这样就可以在不等待的情况下提取值并将其打印出来。没有什么可以等待的;任务已经完成!
现在从这个方法返回什么?没有。它是一种无效方法。所以我们现在回来了,完成了该方法的所有工作。
接下来会发生什么?传递给原始任务的委托已经完成在工作线程上运行,因此在工作线程上运行的任务完成并向主线程发出信号,告知它可以停止等待。
答案 1 :(得分:1)
有几个问题。
你在这里没有开始任何异步。 async-await不会自动使事物异步或创建任何线程。阅读stephen's introductory article on the subject
要卸载某些作品,您需要使用Task.Run
或Task.Factory.StartNew
。
您正在使用async void
方法;没有简单的方法可以等待它的完成。您应该使用async Task
代替。 Read 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
已经向您提出了启动任务。
鉴于两次更改,您的代码应按预期工作
答案 2 :(得分:1)
我在这里看到两个大问题:
FIRST是你对Task.FromResult(result)的使用:它只不过是包装结果所以它看起来像一个Task,所以你可以使用Task的方法。当您致电return await Task.FromResult(GetIntSync());
时,请:
GetIntSync
,int
,FromResult
,Task<int>
,await
,以便立即继续,Task<int>
。 GetIntAsync根本不是异步的!第二个大问题是你正在使用Task
混合测试你的异步/等待技能(也使用Task.Run
)和并行执行。异步执行意味着线程的执行顺序不一定与代码顺序同步。您可以尽快在异步方法中执行代码。异步方法可以等待,将控制权交还给其调用者,直到它表示它已准备好再次工作。这样做的目的是使线程和CPU保持忙碌。为了说明,让我们使用我最喜欢的机制:metaphores!
让逻辑处理器成为桌面工作者。每次切片时,你都会走到一个带有文件夹(方法)的桌子(线程)上,一个接一个地处理其中的表格(线条)。如果一行是一种方法,你抓住该文件夹,处理里面的表格,然后回到你正在处理的文件夹。
然后一张表格X需要你老板的签名。你把它交给你的老板:异步方法Task task = GetBossToSign(Form X)
是从Worker.WorkDay()
调用的。然后,您可以处理其他表单。当你带着签名回到X后,你就完成了X,然后继续你所处的位置,而不是浪费一秒钟。
但现在你来到一个需要X完成的表格Y.幸运的是,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);
答案 3 :(得分:0)
在您的代码中
public static int GetIntSync()
Getintsync只是一个正常的无聊例程,你在写第一行之前调用它,所以它做了所有的工作,然后说,我正在做其他事情。
该示例有效,因为调用的函数是异步函数,因此使用
进行设置Task<T> something = myAsyncFunc(params)
Now output im going to do something
T result = await myAsyncFunc..
现在结果做了其他事情。
就像设置一个deligate
第一行基本上是一个deligate,所以不会对你的代码采取行动。你的命中代码在等待线上做了我。
我的ipads所以不是最好的地方,但希望能看到你的错误以及如何解决它