3

I have an ASP.NET MVC 3 site that connects to a WCF service. The WCF Service is independent from the site and is hosted in a Windows Service. Most of the calls are synchronous, so it's not a problem to wait for the WCF to do it's thing.

However, one of those (already implemented) calls takes a bit too long, and, as it essentially does not output anything directly, I wanted to spin it on the service and forget about it.

So I changed my code from:

public ViewResult StartSlowCalculation(CalculationOptions calculationOptions)
{
  WcfServiceProxy.DoSlowCalculation(calculationOptions);
  ViewBag.Started = true;
  return View();
}

to

public ViewResult StartSlowCalculation(CalculationOptions calculationOptions)
{
  Task.Run(() =>
  {
    WcfServiceProxy.DoSlowCalculation(calculationOptions);
  });

  ViewBag.Started = true;
  return View();
}

which, as I understand should start an asynchronous request, and return immediately. Still, the execution is completely synchronous, and the UI is frozen until the operation concludes.

What obvious thing am I missing?


Update:

Also, note that I would prefer not to change the server implementation to an async one, just to de-synchronize the call to the service on the call-site.

Moreover, I've noticed that the StartSlowCalculation method finishes executing, but the server does not return a response until the service method finishes executing.

The WCF Service Proxy just does:

public void DoSlowCalculation(CalculationOptions calculationOptions)
{
   //some logging code
   Channel.DoSlowCalculation(calculationOptions);
}

so it's completely synchronous, however that shouldn't matter as it should be executed on an independent thread.

SWeko
  • 30,434
  • 10
  • 71
  • 106
  • 1
    take a look at this article which mentions using the async keyword: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx – Ric Dec 27 '12 at 11:29
  • It looks about right to me, it shouldn't cause anything to hang - unless there are other calls to the WCF service being made that are being forced (for whatever reason) to be serialized with respect to the long running calculation, and it's waiting for one of *those* calls to complete that is causing the hang. – Damien_The_Unbeliever Dec 27 '12 at 11:31
  • This is the only call active at the time (I actually have some orchestration and queuing in the service that takes care of that) – SWeko Dec 27 '12 at 11:34
  • Is the service on the same machine as the GUI? Do you have more than one processor/core? Maybe it is just using all the available resources anyway. – Eyvind Dec 27 '12 at 11:35
  • @Eyvind No, the system load is very light, both in memory and in processor time. – SWeko Dec 27 '12 at 11:46
  • General comment - IIS and ASP.NET are for request-based processing, i.e. process a request, and you're done. Starting other things or doing long-running processes (even waiting for a call to return) in the context of a website/IIS is not the correct way to do things. You'd ideally post to a message queue and poll for a result, or communicate with a Windows Service which acts as a proxy for handling those long-running calls. Having threads hanging around like that isn't suitable for production use. – Kieren Johnstone Dec 27 '12 at 11:46
  • @KierenJohnstone: "communicate with a Windows Service which acts as a proxy for handling those long-running calls", that's exactly what I am doing. – SWeko Dec 27 '12 at 11:48
  • No, that's an MVC app calling a WCF service and waiting for the result.. isn't it? – Kieren Johnstone Dec 27 '12 at 11:48
  • @KierenJohnstone: please read the question. The WCF is a self-hosted windows service, and I explicitly do not want to wait for the result. – SWeko Dec 27 '12 at 11:54
  • It's not exactly waiting for a result, but it is tying up a worker thread that asp.net would like to be using for handling requests. – Damien_The_Unbeliever Dec 27 '12 at 11:55
  • I did read it, thanks :) You don't say it's hosted in a Windows Service, and you are having a worker process thread wait for the result as Damien says. My point is, don't call a method that will wait for the result - which is what you are doing with `DoSlowCalculation`. You could create a method on the proxy service, "BeginSlowCalculation", which spins up and manages its own thread - but do that in the Windows Service, not the MVC app. – Kieren Johnstone Dec 27 '12 at 12:01
  • @KierenJohnstone: I'll edit the question to better explain my situation. – SWeko Dec 27 '12 at 12:28
  • Ah I see, yes, what I am saying applies: don't have a long-running client call. Poll the server if necessary.. – Kieren Johnstone Dec 27 '12 at 13:15
  • Is there any code in the view that might be worth adding also? – Damien_The_Unbeliever Dec 27 '12 at 13:17

2 Answers2

2

A task operation can run in the calling thread, it depends on taskScheduler decision. To help TaskScheduler make a right decision regarding long running call you can specify task creation option TaskCreationOptions.LongRunning.

And you can check whether task operation is running in a separate thread:

int launchedByThreadId = Thread.CurrentThread.ManagedThreadId;
int launchedInThreadId = -1;
Task.Run(() =>
  {
    launchedInThreadId = Thread.CurrentThread.ManagedThreadId;
    WcfServiceProxy.DoSlowCalculation(calculationOptions);
  });

// then compare whether thread ids are different

BTW, are you using any kind of Task.Wait() operation? It will block calling thread as well.

EDIT:

You might find following post interesting Is Task.Factory.StartNew() guaranteed to use another thread than the calling thread?

So try out using Task.Factory.StartNew() and specify cancellation token even you do not need it, sounds weird but it seems this guarantees that task will not be run eventually in the calling thread. Correct me If I wrong.

Community
  • 1
  • 1
sll
  • 61,540
  • 22
  • 104
  • 156
  • Will try TaskCreationOptions.LongRunning to create a dedicated thread. And no, the started task is not waited or awaited at all. – SWeko Dec 27 '12 at 11:45
  • They are running on different threads both with or without TaskCreationOptions.LongRunning, and they still hang. I've noticed that the Action method finishes executing, but the server does not return a response until the service method finishes executing. – SWeko Dec 27 '12 at 12:52
  • Are you using Async WCF call? (APM pattern implementation by specifying Begin/End WCF methods). Could you share WCF service contract? – sll Dec 27 '12 at 13:20
  • No, the WCF call is synchronous, and I'm trying to avoid changing the server code. – SWeko Dec 27 '12 at 13:42
0

I've done this before.

The most robust way would be to use Asynchronous Controller's, or better yet an independant service such as a WCF service.

But in my experience, i've just needed to do "simple", one-liner task, such as auditing or reporting.

In that example, the easy way - fire off a Task:

public ViewResult StartSlowCalculation(CalculationOptions calculationOptions)
{
  //Some Synchronous code.

   Task.Factory.StartNew(() =>
  {
    WcfServiceProxy.DoSlowCalculation(calculationOptions);
  });

  ViewBag.Started = true;
  return View();
}

That's a simple example. You can fire off as many tasks as you want, synchronize them, get notified when they finish, etc.

For more details you can see this links.

https://msdn.microsoft.com/en-us/library/dd321439(v=vs.110).aspx

Jenish Zinzuvadiya
  • 999
  • 2
  • 15
  • 29