如何触发异步任务,但在返回ActionResult之前等待所有回调?

时间:2011-06-01 00:12:15

标签: c# asp.net-mvc asp.net-mvc-3 asynchronous asynccallback

我有一个ASP.NET MVC 3操作方法,它接受HTTP POST中的HttpFileCollectionBase

在这种方法中,我需要调整大小并上传图像3次。

动作方法目前如下所示:

public ActionResult ChangeProfilePicture()
{
   var fileUpload = Request.Files[0];

   ResizeAndUpload(fileUpload.InputStream, Size.Original);
   ResizeAndUpload(fileUpload.InputStream, Size.Profile);
   ResizeAndUpload(fileUpload.InputStream, Size.Thumb);

   return Content("Success", "text/plain");   
}

基本上这是一个用户个人资料页面,他们在这里更改了他们的个人资料照片。上传通过jQuery AJAX发生。

现在,我如何将三个ResizeAndUpload调用作为异步任务启动,但是在完成所有三个任务之前不返回操作结果?

以前我一直在使用Task.Factory.StartNew来启动异步任务,但那时我并不关心等待结果。

有什么想法吗?

6 个答案:

答案 0 :(得分:7)

一种简单的方法是使用Join:

public ActionResult ChangeProfilePicture()
{
   var fileUpload = Request.Files[0];
   var threads = new Thread[3];
   threads[0] = new Thread(()=>ResizeAndUpload(fileUpload.InputStream, Size.Original));
   threads[1] = new Thread(()=>ResizeAndUpload(fileUpload.InputStream, Size.Profile));
   threads[2] = new Thread(()=>ResizeAndUpload(fileUpload.InputStream, Size.Thumb));

   threads[0].Start();
   threads[1].Start();
   threads[2].Start();

   threads[0].Join();
   threads[1].Join();
   threads[2].Join();

   return Content("Success", "text/plain");   
}

虽然ResizeAndUpload方法可能会在某处阻塞(无法在不看代码的情况下无法确定),但在这种情况下,重构这些也可能是值得的,以使它们异步。

答案 1 :(得分:6)

还使用Task.Factory.StartNew使其工作,类似于@BBree的回答:

public ActionResult ChangeProfilePicture()
{
   var fileUpload = Request.Files[0];
   var threads = new Task[3];
   threads[0] = Task.Factory.StartNew(()=>ResizeAndUpload(fileUpload.InputStream, Size.Original));
   threads[1] = Task.Factory.StartNew(()=>ResizeAndUpload(fileUpload.InputStream, Size.Profile));
   threads[2] = Task.Factory.StartNew(()=>ResizeAndUpload(fileUpload.InputStream, Size.Thumb));

   Task.WaitAll(threads, 120000); // wait for 2mins.

   return Content("Success", "text/plain");   
}

现在确定ThreadTask是否更好。

答案 2 :(得分:1)

使用Task和ManualResetEvent的不同实现

public ActionResult Sample()
    {
        var wh1 = new ManualResetEvent(false);
        var wh2 = new ManualResetEvent(false);
        var wh3 = new ManualResetEvent(false);

        Task.Factory.StartNew(new Action<object>(wh =>
        {
            // DoSomething();
            var handle = (ManualResetEvent)wh;
            handle.Set();
        }), wh1);

        Task.Factory.StartNew(new Action<object>(wh =>
        {
            // DoSomething();
            var handle = (ManualResetEvent)wh;
            handle.Set();
        }), wh2);

        Task.Factory.StartNew(new Action<object>(wh =>
        {
            // DoSomething();
            var handle = (ManualResetEvent)wh;
            handle.Set();
        }), wh3);

        WaitHandle.WaitAll(new[] { wh1, wh2, wh3 });

        return View();
    }

希望这会有所帮助。

答案 3 :(得分:1)

这是我对它的看法,使用方便的“Task.WaitAll”静态方法进行等待..

public MyViewModel LoadData()
{
    MyViewModel viewModel = null;

    try
    {
        Task.Factory.StartNew(() =>
           {
               var task1 = Task<MyViewModel>.Factory.StartNew(() =>
               {
                   return BuildMyViewModel(args);
               });

               var task2 = Task<ViewModel2>.Factory.StartNew(() =>
               {
                   return BuildViewModel2(args);
               });

               var task3 = Task<ViewModel3>.Factory.StartNew(() =>
               {
                   return BuildViewModel3(args);
               });

               Task.WaitAll(task1, task2, task3);

               viewModel = task1.Result;

               viewModel.ViewModel2 = task2.Result;
               viewModel.ViewModel3 = task3.Result;

           }).Wait();
    }
    catch (AggregateException ex)
    {
        System.Diagnostics.Trace.WriteLine(ex.StackTrace); 
        // ...
    }

    return viewModel;
}

答案 4 :(得分:0)

MVC操作还有一个异步模式,请参阅以下链接:

http://msdn.microsoft.com/en-us/library/ee728598.aspx

您仍然可以使用“任务”,但不需要任何特殊处理来允许操作执行异步

答案 5 :(得分:0)

你可以用子任务来组成一项任务。当所有子任务完成时,父任务将完成。

示例:

public ActionResult ChangeProfilePicture()
{
    var fileUpload = Request.Files[0];

    Task.Factory.StartNew(() =>
    {
        Task.Factory.StartNew(() => 
            ResizeAndUpload(fileUpload.InputStream, Size.Original), 
            TaskCreationOptions.AttachedToParent);

        Task.Factory.StartNew(() => 
            ResizeAndUpload(fileUpload.InputStream, Size.Profile), 
            TaskCreationOptions.AttachedToParent);

        Task.Factory.StartNew(() => 
            ResizeAndUpload(fileUpload.InputStream, Size.Thumb), 
            TaskCreationOptions.AttachedToParent);
    }).Wait();

    return Content("Success", "text/plain");
}