Как запустить асинхронную задачу, но дождаться всех обратных вызовов, прежде чем возвращать ActionResult? - PullRequest
3 голосов
/ 01 июня 2011

У меня есть метод действия ASP.NET MVC 3, который принимает HttpFileCollectionBase в HTTP POST.

В этом методе мне нужно изменить размер и загрузить изображение 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 ]

7 голосов
/ 01 июня 2011

Один простой способ сделать это, используя 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 где-то блокирует (не может сказать наверняка, не увидев код), в этом случае онможет быть целесообразно провести их рефакторинг, чтобы сделать их асинхронными.

6 голосов
/ 01 июня 2011

Также работает с использованием Task.Factory.StartNew, аналогично ответу @ BFree:

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");   
}

Теперь убедитесь, что Thread или Task лучше.

1 голос
/ 15 февраля 2012

Вот мое мнение, используйте удобный статический метод "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;
}
1 голос
/ 01 июня 2011

Различные реализации, которые используют 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();
    }

надеюсь, это поможет.

0 голосов
/ 19 июня 2013

Вы можете составить задачу с дочерними задачами.Родительская задача будет завершена после завершения всех дочерних задач.

Пример:

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");
}
0 голосов
/ 01 июня 2011

Существует также асинхронный шаблон для действий MVC, см. Эту ссылку:

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

вы все еще можете использовать Задачи, но не нужно никакой специальной обработки, чтобы разрешить действиявыполнить асинхронное

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...