0

I have a web api 2 controller, the client requests the controller for some kind of html generations(consists of images, files etc.). As the process needs some time and I don't want the users to wait, I have followed the following approach in the Controller :

...Controller(){
Task.Run(() =>
        {
          //calling heavy duty method to download files where Task.WaitAll() resides
          DownloadAndRename()
        });
}

DownloadAndRename(){
     //created some child task here and run them in task.waitall()
     //Task.WaitAll() here
}

I have created tasks for every file to download, rename and other processes. Then execute them in Task.WaitAll().

When I locally run the application, everything is okay. But when I deploy to my test server, it was throwing following exception:

Message: System.Threading.ThreadAbortException: Thread was being aborted.

P.S. In my pc, I have only one application running in the app pool, whereas in my test server there are 8 in that particular pool.

How can I overcome the exception?

Update 1: I have tried by removing the task.WaitAll and do the implementation without using Task. Still, I get the thread abort error, I think somehow the Task.Run() get timed out.

lukai
  • 536
  • 1
  • 5
  • 20
  • You could look at other options like [Hangfire](https://www.hangfire.io/). It is designed to execute long running processes inside asp.net framework / asp.net core. – Silvermind Oct 17 '18 at 12:43
  • If you want to run DownloadAndRename() asynchronously, you could pass the Task returned by Task.Run() into Task.WaitAll(). Task.WaitAll() would need to be called inside the Controller() scope. Note that Task.WaitAll() will block the calling thread, however. Is it possible to make the Controller method async and await DownloadAndRename() instead? That'll ensure that the calling thread isn't blocked. – Jamie Butterworth Oct 17 '18 at 12:50
  • @JamieButterworth, I have tried by removing the task.WaitAll, and do the implementation without using Task. Still I get the thread abort error, I think somehow the Task.Run() get timed out. – lukai Oct 17 '18 at 12:56
  • The problem is, asp.net isn't "aware" of what you're doing with tasks and several of its mechanisms around recycling, etc only try to protect *requests* and things linked to them. It seems this really would be better placed in a separate executable, such as a windows service, that then the asp.net application interacts with via some form of IPC. – Damien_The_Unbeliever Oct 17 '18 at 13:01
  • This code guarantees a ThreadAbortException. `Task.Run` without awaiting starts a task and simply forgets about it. `Task.WaitAll()` ensures this task will be still there when ASP.NET terminates the thread at the end of the request, or when it recycles. There are a *LOT* of articles that explain how to start long-running jobs in ASP.NET or ASP.NET Core. Which one do you target? – Panagiotis Kanavos Oct 17 '18 at 13:01
  • Can you use make the Action Method `async` and `await` each download and rename operation ? – karthickj25 Oct 17 '18 at 13:10
  • @PanagiotisKanavos, then why it works in my local machine? I have started to follow pluralsight Asynchronus programming in C# – lukai Oct 18 '18 at 05:12
  • It doesn't. You didn't let the site run long enough for the app pool to recycle. There's no ambiguity about this. It's well explained in [The Dangers of Implementing Recurring Background Tasks In ASP.NET](https://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx/) and [How to run Background Tasks in ASP.NET](https://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx). – Panagiotis Kanavos Oct 18 '18 at 06:45
  • On the other hand, if you *don't* want to start a background task, there's no reason to use `Task.Run` *at all*. Each request is served by its own thread. Might as well use it for the download. Your code spins up another thread to do something the first one could easily have done. `async/await` in web applications is used for *really asynchronous operations* that don't need to block a thread. `async/await` in this case release the request thread while awaiting for the asynchronous process to finish, making it available to serve other requests – Panagiotis Kanavos Oct 18 '18 at 06:47

1 Answers1

0

I think the issue is a simple misunderstanding of task implementation.

If you want the task to run immediately as a background task, you can add async keyword and Task return type to the Controller action and make DownloadAndRename() async and make it return a Task and then await DownloadAndRename() from inside the controller action scope. The calling thread won't be blocked this way and any code after the task will not be executed until the returned task is complete.

async Task Controller()
{
await DownloadAndRename();
}

If that still doesn't work after removing Task.WaitAll(), I'd have a read of this as it might genuinely be timing out as you said or could be exceptioning for another reason: Why am i getting "Thread was being aborted" in asp.net?

  • I have followed the second option already, but it fails. I am wondering why it works in my local machine but not in my dev server. – lukai Oct 18 '18 at 05:14
  • The second option is pointless and simply won't work - IIS will terminate that thread at some point, unless the operation just happens to finish in time. It's also pointless - why spin up a *another* thread when the current one could perform the operation itself? `async/await` in web applications is used to improve scalability, not to avoid blocking. There's no UI thread to block, all requests get served by a different thread – Panagiotis Kanavos Oct 18 '18 at 06:50
  • @PanagiotisKanavos Thanks. I've read through Phil Haack's article and now understand the dangers of running a task like in example 2 so I've removed it from my answer. – Jamie Butterworth Oct 18 '18 at 15:23
  • @PanagiotisKanavos, I have some operations to be done in controller upon completion of DownloadAndRename method, making my controller async is blocking my UI thread. I'm not sure what is going wrong. Maybe I need to study more regarding this. – lukai Oct 19 '18 at 05:24
  • 2
    @lukai there is no UI thread in web applications. Making the controller `async` won't block anything. If you want your action to complete but leave the job running in the background you have a **background task**. You need to use `QueueBackgroundWorkItem` to tell IIS about it and not abort the thread – Panagiotis Kanavos Oct 19 '18 at 07:46