C# Task.ContinueWith执行顺序

C# Task.ContinueWith执行顺序,c#,task,asp.net-web-api,C#,Task,Asp.net Web Api,显然,我不明白如何使用ContinueWith方法。我的目标是执行一项任务,完成后返回一条消息 这是我的密码: public string UploadFile() { if (Request.Content.IsMimeMultipartContent()) { //Save file MultipartFormDataStreamProvider provider = new MultipartF

显然,我不明白如何使用ContinueWith方法。我的目标是执行一项任务,完成后返回一条消息

这是我的密码:

    public string UploadFile()
    {
        if (Request.Content.IsMimeMultipartContent())
        {
            //Save file
            MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath("~/Files"));
            Task<IEnumerable<HttpContent>> task = Request.Content.ReadAsMultipartAsync(provider);

            string filename = "Not set";

            task.ContinueWith(o =>
            {
                //File name
                filename = provider.BodyPartFileNames.First().Value;
            }, TaskScheduler.FromCurrentSynchronizationContext()); 

            return filename;
        }
        else
        {
            return "Invalid.";
        }
    }
公共字符串上传文件()
{
if(Request.Content.IsMimeMultipartContent())
{
//保存文件
MultipartFormDataStreamProvider provider=新的MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath(“~/Files”);
Task Task=Request.Content.ReadAsMultipartAsync(提供程序);
字符串filename=“未设置”;
task.ContinueWith(o=>
{
//文件名
filename=provider.BodyPartFileNames.First().Value;
},TaskScheduler.FromCurrentSynchronizationContext());
返回文件名;
}
其他的
{
返回“无效。”;
}
}
变量“filename”总是返回“notset”。似乎从未调用ContinueWith方法中的代码。(如果我在VS中逐行调试它,它确实会被调用。)

此方法正在我的ASP.NET Web API controller/Ajax文章中调用


这里我做错了什么?

您应该从方法返回类型
Task
,在这种情况下,它将是一个
Task
您正在使用异步操作。如果要等待任务完成,则必须使用
wait
方法,否则您的任务:

task.ContinueWith(o =>
        {
            //File name
            filename = provider.BodyPartFileNames.First().Value;
        ).Wait();

return filename;
编辑:
一些异步方法在任务创建后立即启动任务,而另一些方法则要求您显式启动它们。您必须查阅每一项的文档才能确定。在这种情况下,任务似乎会自动启动。

未设置变量的原因如下:

  • 任务已实例化,但未运行
  • 即使任务运行了,函数也可能在任务运行结束之前返回,因此,它仍然会返回“notset”。对此的修复正在等待最终任务(设置文件名的任务)完成
您的代码可以按如下方式修复:

public string UploadFile()
{
    if (Request.Content.IsMimeMultipartContent())
    {
        //Save file
        MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath("~/Files"));
        Task<IEnumerable<HttpContent>> task = Request.Content.ReadAsMultipartAsync(provider);

        string filename = "Not set";

        var finalTask = task.ContinueWith(o =>
            {
                //File name
                filename = provider.BodyPartFileNames.First().Value;
            }, TaskScheduler.FromCurrentSynchronizationContext()); 

        task.Start();

        finalTask.Wait();

        return filename;
    }
    else
    {
        return "Invalid.";
    }
}

免责声明:我没有实际执行上述代码;我写它只是为了说明应该做什么。

如果您使用的是异步操作,最好的方法是使您的操作也异步,否则您将失去正在进行的异步调用的优势。尝试按如下方式重写您的方法:

public Task<string> UploadFile()
{
    if (Request.Content.IsMimeMultipartContent())
    {
        //Save file
        MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath("~/Files"));
        Task<IEnumerable<HttpContent>> task = Request.Content.ReadAsMultipartAsync(provider);

        return task.ContinueWith<string>(contents =>
        {
            return provider.BodyPartFileNames.First().Value;
        }, TaskScheduler.FromCurrentSynchronizationContext()); 
    }
    else
    {
        // For returning non-async stuff, use a TaskCompletionSource to avoid thread switches
        TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
        tcs.SetResult("Invalid.");
        return tcs.Task;
    }
}
public任务上传文件()
{
if(Request.Content.IsMimeMultipartContent())
{
//保存文件
MultipartFormDataStreamProvider provider=新的MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath(“~/Files”);
Task Task=Request.Content.ReadAsMultipartAsync(提供程序);
返回任务。继续(内容=>
{
返回provider.BodyPartFileNames.First().Value;
},TaskScheduler.FromCurrentSynchronizationContext());
}
其他的
{
//要返回非异步内容,请使用TaskCompletionSource避免线程切换
TaskCompletionSource tcs=新的TaskCompletionSource();
tcs.SetResult(“无效”);
返回tcs.Task;
}
}

这是因为您正在执行异步操作。此外,除了异步任务之外,我认为它们甚至没有启动。您不应该调用
Wait()
,因为这将导致线程阻塞。相反,这个
UploadFile()
函数本身应该是异步的。我尝试了您的第一个建议,但在start方法上出现了一个错误:不能对具有null操作的任务调用start。关于非异步-我不确定。我不认为有一种叫做ReadAsMultipart(AFAIK)的方法。@marcind你是对的,这是一种笨拙的构造。这就是我建议同步替代方案的原因,如果可能的话。这是因为我们有两个任务是按顺序执行的,当它们完成时我们需要控制——我认为这里没有异步现象。@Rivka是的,我没说它有效,我只是建议你尝试找到一个更简单的替代方案。异步代码是不需要的,在这种情况下只会增加复杂性。我尝试了这个,但我有相同的问题-没有设置“文件名”。尝试了这个。使用Wait()时,它似乎不会返回任何内容(甚至“未设置”),这意味着任务在创建时不会运行。我相应地编辑了我的答案。对于具有null操作的任务,可能无法调用Get the error Start。任务的状态为“RanToCompletion”。这意味着任务已启动(并已完成…)。延续应该马上开始。如果没有,则使用的调度程序可能无法安排继续。我会尝试从ContinueWith调用中删除调度程序。在这种情况下,Wait将阻止线程,因此不会执行ContinueWith。哇,谢谢!这对我有用。也要感谢所有人-感谢您的帮助。我对TaskScheduler.FromCurrentSynchronizationContext有一个问题-我的理解是,这确实是一个UI方法(与UI线程同步),但我想知道是否有任何理由可以在webapi方法中使用。是否有?同步上下文还可用于存储一些线程局部变量,并确保在控件返回到continuation时重新填充这些变量。例如
线程.CurrentPrincipal
。如果我没记错的话,ASP.NET运行时也为这种情况定义了同步上下文。如果您使用的是.NET 4.0-请参阅以下线程:
public Task<string> UploadFile()
{
    if (Request.Content.IsMimeMultipartContent())
    {
        //Save file
        MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath("~/Files"));
        Task<IEnumerable<HttpContent>> task = Request.Content.ReadAsMultipartAsync(provider);

        return task.ContinueWith<string>(contents =>
        {
            return provider.BodyPartFileNames.First().Value;
        }, TaskScheduler.FromCurrentSynchronizationContext()); 
    }
    else
    {
        // For returning non-async stuff, use a TaskCompletionSource to avoid thread switches
        TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
        tcs.SetResult("Invalid.");
        return tcs.Task;
    }
}