How can you use HttpContext.Current with multithreading?

To clarify my question, I developed an application that performs a lot of database / web service call updates based on user input (using an Excel spreadsheet). If there are many updates, so that the process can take more than 20 minutes.

To stop my user interface from freezing / timeout, I was looking for multithreading, so I can start my long-running process asynchronously and, at the same time, just display an animated gif during the process.

All this seems to work well at the moment with my test data, but when I replace the actual lengthy process, I get the HttpContext.Current.User.Identity.Name error message. I read about it and from this article1 I realized that if you set the β€œAsync” property to β€œtrue” in the page directive and used the RegisterAsyncTask method with which you could access HttpContext.Current. However, for me this is not like the truth. I am sure that this is what I am doing, so here is my code (basically I used the following articles to write article2 and article3 ):

ASP.NET page

<%@ Page Title="Home Page" Async="true" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="false" CodeBehind="Index.aspx.cs" Inherits="MyApp.Index" %> 

C # - RegisterAsyncTask is executed by pressing a button that starts a lengthy process:

 protected void ProcessUpdates() { //Register async task to allow the processing of valid updates to occurr in the background PageAsyncTask task = new PageAsyncTask(OnBegin, OnEnd, OnTimeOut, null); RegisterAsyncTask(task); } IAsyncResult OnBegin(Object sender, EventArgs e, AsyncCallback cb, object state) { return Worker.BeginWork(cb, state); } private void OnEnd(IAsyncResult asyncResult) { //UpdateResults list should now have been filled and can be used to fill the datagrid dgProcessedUpdates.DataSource = Worker.UpdateResults; dgProcessedUpdates.CurrentPageIndex = 0; dgProcessedUpdates.DataBind(); lblProgress.Text = "Update Results: update success / failure is shown below"; } private void OnTimeOut(IAsyncResult asyncResult) { lblProgress.Text = "The process has timed out. Please check if any of the updates have been processed."; } 

C # - Work Class

 public class Worker { public static List<AuditResult> UpdateResults = new List<AuditResult>(); private delegate void del(); //This method is called when the thread is started public static IAsyncResult BeginWork(AsyncCallback cb, object state) { del processing = DoUpdateProcessing; return processing.BeginInvoke(cb, state); } private static void DoUpdateProcessing() { //UpdateResults = ExcelFileProcessing.PassValidUpdates(); //Testing Thread.Sleep(5000); int i = 0; while(i < 10) { AuditResult ar = new AuditResult(); ar.Result = "Successful"; ar.JobNumber = (1000 + i).ToString(); ar.NewValue = "Test New Value " + i.ToString(); ar.ResultDate = DateTime.Now.ToString(); ar.UserName = HttpContext.Current.User.Identity.Name; UpdateResults.Add(ar); i++; } } } 

At first, my test code did not include the HttpContext.Current.User.Name call for ar.UserName, but after my problems returning to the ExcelFileProcessing.PassValidUpdates () call, I decided to do this. When I get to this part (ar.UserName = HttpContext.Current.User.Identity.Name), it says: β€œThe reference to the object is not set to the instance of the object”, which indicates that the HttpContext is not transferred to the second stream . How can i do this?

UPDATE

Currently, I reverted to my previous code (which initially did not work) and just passed HttpContext.Current as a variable to my DoWork method according to this question https://stackoverflow.com/a/3606263 :

Create a second thread

  protected void ProcessValidUpdates() { Worker workerObject = new Worker(); HttpContext ctx = HttpContext.Current; Thread workerThread = new Thread(new ThreadStart(() => { HttpContext.Current = ctx; workerObject.DoWork(); })); workerThread.Start(); //Loop until worker thread activates while (!workerThread.IsAlive) ; //Put main thread to sleep to allow the worker thread to do some work Thread.Sleep(1000); //Request the worker thread stop itself workerObject.RequestStop(); //Use the Join method to block the current thread until the object thread terminates workerThread.Join(); //UpdateResults list should now have been filled and can be used to fill the datagrid dgProcessedUpdates.DataSource = Worker.UpdateResults; dgProcessedUpdates.CurrentPageIndex = 0; dgProcessedUpdates.DataBind(); lblProgress.Text = "Update Results: update success / failure is shown below"; } 

Working class

 public class Worker { //volatile hints to the compiler that this data member will be accessed by multiple threads. private volatile bool _shouldStop; public static List<AuditResult> UpdateResults = new List<AuditResult>(); //This method is called when the thread is started public void DoWork() { while (!_shouldStop) { //Testing Thread.Sleep(5000); int i = 0; while (i < 10) { AuditResult ar = new AuditResult(); ar.Result = "Successful"; ar.JobNumber = (1000 + i).ToString(); ar.NewValue = "Test New Value " + i.ToString(); ar.ResultDate = DateTime.Now.ToString(); ar.UserName = HttpContext.Current.User.Identity.Name; UpdateResults.Add(ar); i++; } } } public void RequestStop() { _shouldStop = true; } } 

It looks like I can now access the HttpContext.Current and the expected username. I think this is probably to some extent what some of you have suggested anyway. I appreciate the solution proposed by Andrew Morton, but at the moment this will require significant rewriting. Right now, my process is already calling a web service to create the database and returning the result of success or failure. You must also call another BPEL service. As such, I suspect that further performance hits may occur if I have to transfer all this to another web service. In addition, most process calls will not be long (probably less than 10 minutes), so this is only valid for resolving multiple requests that exceed 20 minutes. Finally, it is most likely to be used by 1 or 2 people, so it is unlikely to have a huge number of requests 1 time.

However, bearing in mind my current decision, is there anything I should know about that can knock me over? Is IIS causing problems? Any additional help would be greatly appreciated.

+8
multithreading c #
source share
1 answer

I have a site on a shared server. I need to have a BATCH job, and I'm doing it in a different thread. It can work up to 1 hour (I ping the site so that the workflow does not stop).

I followed the binding path to get the current context. After many hours of research and searching, this is not possible. Httpcontent.current does not exist in the new stream, it is not the stream that the user accessed, so the context is not migrated, and you cannot access the registered user, since they are not included in this stream.

+1
source

All Articles