-1

I have mvc action method where while loop is running I want to stop that while loop on another button click so I have maintain a flag and set it to false to break loop.

bool flag = true;

public async Demo()
{
while(flag == true)
{
do something...
}
}

now on button click I'm calling one action method to stop the while loop -

    public ActionResult StopLoop()  
    {  
        flag = false;
        Return View("Index")
    }  

But it is taking almost a minute time to hit this StopLoop after click on button.

why ? may be all are on same UI page and sharing same homecontroller any trick i can do ?

i have 2 buttons on one button request while loop start and i want on another button click request loop stop.

Neo
  • 15,491
  • 59
  • 215
  • 405
  • 1
    Two tips: 1) `StopLoop` executed in another thread: try to make the `flag` field `volatile`. 2) your _do something_ takes long and you poll the flag too rarely – György Kőszeg Oct 29 '18 at 12:06
  • 5
    Your mvc app is stateless so when you set `flag = false` is is not the same instance of `flag` as when the first response was sent. – Crowcoder Oct 29 '18 at 12:06
  • try to pass the flag value to UI in return and get back and read it in controller. may be `TempData` will be helpful – Ameya Deshpande Oct 29 '18 at 12:15
  • In any web application (any language, any stack), different requests have different state/context. Perhaps the *real* question is how to run a long-running job in ASP.NET MVC? – Panagiotis Kanavos Oct 29 '18 at 12:16
  • @AmeyaDeshpande it won't. There's no way an *older* request can know about variables and data used by a *future* request. Besides, the loop will be killed by IIS itself once the `Demo()` action returns – Panagiotis Kanavos Oct 29 '18 at 12:17
  • 3
    @Neo check [How to run Background Tasks in ASP.NET](https://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx). It's not only that your code can't be cancelled by another action, it probably won't even run long enough. Even in a desktop application trying to access a global field like that is guaranteed to fail with race conditions – Panagiotis Kanavos Oct 29 '18 at 12:19
  • this is great example can you put little code snippet in answer i want to mark as answer – Neo Oct 29 '18 at 12:36

2 Answers2

3

If you're saying that one request starts the while loop, and you want another request to stop it, then that's not possible with a ASP.NET MVC as it is stateless. Once the view is returned your while loop is no longer running. In your case, it only looks like it's taking a minute for the StopLoop method to get hit. What's probably really happening is that your while loop is probably running endlessly, IIS kills it, and then the stop loop request is processed.

Bruno
  • 533
  • 1
  • 6
  • 27
  • right now executing it for small number thats why it is taking minute , i'm worried for large scale it will will be endless :( is there any other way to stop loop ? – Neo Oct 29 '18 at 13:30
  • i have 2 buttons on one button request while loop start and i want on another button click request loop stop. – Neo Oct 29 '18 at 13:32
  • 1
    In MVC you can't click a button to start a process then click another button to stop it. If you were working with a desktop application you could totally do what you're describing because you'd have complete control of all threads. In MVC once you return from the controller you have lost control of all threads and can't interact with them any more. Your requests don't persist on the server after you return to the view. – Bruno Oct 29 '18 at 13:38
  • 1
    I'd recommend reading [this](https://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx) article by Scott Hanselman. In it he talks about a few different options for dealing with jobs that run independently of your site(Azure Webjobs, etc.) This might point you in the right direction with regard to running background processes as part of your application. – Bruno Oct 29 '18 at 13:45
  • but he is using nuget `WebBackgrounder` why to use that :( its old one , isn't there any other similar way to achieve it ? thanks in advance – Neo Oct 29 '18 at 15:05
  • `WebBackgrounder` is only one of the tools mentioned in the post. He also mentions tools like `Hangfire`, `Azure Webjobs`, and a few others. You could look into a few of those tools and see if any of them are a good fit for your scenario. Just out of curiosity, why do you need to stop the process after it starts? Is this something you could "fire and forget"? – Bruno Oct 29 '18 at 20:32
  • thanks :) can you add a code sample for hangfire which i can use in my scenario ? – Neo Oct 30 '18 at 09:37
  • can i set flag at client side in hidden filed ? what is your opinion ? – Neo Oct 30 '18 at 10:23
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/182795/discussion-between-bruno-and-neo). – Bruno Oct 30 '18 at 12:49
1

if you want run two tasks parallel, you need to use new thread for second task, also you need to stop second thread conditionally.

CancellationTokenSource TokenSource = new CancellationTokenSource();
CancellationToken Ct = TokenSource.Token;

Task Demo = Task.Run(() =>
{
    do
    {
        Thread.Sleep(1000);
        Trace.Write("loop");
    } while (!Ct.IsCancellationRequested);
}, Ct);

bool Flag = true;

if (Flag)
{
    TokenSource.Cancel();
}

you can see more information about async programming here Creating and running tasks explicitly

and about Task Cancellation


Update

if you want do this in web application you have to run second task in a thread that won't kill after request finished.

public class MvcApplication : System.Web.HttpApplication
{
    static Task Demo = null;
    static CancellationTokenSource TokenSource = null;
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);


        TokenSource = new CancellationTokenSource();
        CancellationToken Ct = TokenSource.Token;

        Demo = Task.Run(() =>
        {
            do
            {
                Thread.Sleep(1000);
                Trace.Write("loop");
            } while (!Ct.IsCancellationRequested);
        }, Ct);
    }

    public static void CancelLoop()
    {
        TokenSource.Cancel();
    }
}

and you have to cancel the second task (contain while loop) with a request. for example:

public class HomeController : Controller
        {
            public JsonResult ButtonPressed()
            {
                MvcApplication.CancelLoop();

                return Json("canceled", JsonRequestBehavior.AllowGet);
            }
        }
Mehrdad
  • 1,523
  • 9
  • 23
  • what is the best approach for web application? – Neo Oct 29 '18 at 12:34
  • you should call the while task in application_start (global.asax) – Mehrdad Oct 29 '18 at 12:59
  • That will only work for small applications. What about applications that run on more than one server farm node? – nvoigt Oct 29 '18 at 13:07
  • i don't have information about server farm,but it will work until you have one web application and application pool on your iis without paying attention to count of servers. – Mehrdad Oct 29 '18 at 13:13
  • my application is on big scale :( – Neo Oct 29 '18 at 13:28
  • i can't understand the specific problem with this code – Mehrdad Oct 29 '18 at 13:36
  • there is no problem but I can't keep my while loop code into start up . application flow is like on button click there is a async method in homecontroller which executes while loop and on stop button by i want to stop that while loop so for this i tried using flag. which takes time :( can you give me example which does not use startup.cs – Neo Oct 29 '18 at 15:09
  • 1
    there is a basic rule that force us for using startup.cs, in web applications after request finished everythings should be cleared from server instead the objects that refrence to startup.cs,if your create a task in a request,after request finished the task will be canceled because the parent task gone – Mehrdad Oct 29 '18 at 17:32
  • I have implemented the same code as yours but inside homecontroller.cs only but still it is taking time to invoked second button event :( – Neo Oct 30 '18 at 11:40