Wednesday, March 28, 2012

updates from another thread

Hi, I'm trying to get a label control to update with dynamic information when a timer does its tick event.

I can get it to update with information hard-coded into the tick event, but i can't seem to get any data from a global variable or other shared resource.

Here's what i have so far-

using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;using System.Threading;public partialclass _Default : System.Web.UI.Page {string temp;protected void Page_Load(object sender, EventArgs e) { }protected void Timer1_Tick(object sender, EventArgs e) { Label1.Text ="Tick at " + DateTime.Now.ToString()+"<br />";if (temp !=null) Label1.Text +="register at-"+temp; }protected void Button1_Click(object sender, EventArgs e) { Thread a =new Thread(Foo); a.Start(); }protected void Foo() {while (true) { Thread.Sleep(100); temp = DateTime.Now.Day.ToString(); } }}

(that's actually a +"<br />" at the end of the first line on the tick event)

and the markup-

<%@dotnet.itags.org. Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head runat="server"> <title>Untitled Page</title></head><body> <form id="form1" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server" /> <div> <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional"> <ContentTemplate> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label> </ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" /> </Triggers> </asp:UpdatePanel> </div> <asp:Timer ID="Timer1" runat="server" Interval="500" OnTick="Timer1_Tick"> </asp:Timer> <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" /> </form> </body></html>
the 'register at" text never fires. I've tried using Session and Cache variables in place of a global string but no luck.

I am not sure how it is not working when puting it in Session/Cache, would you mind posting the code?


Here's the code using session-

using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;using System.Threading;public partialclass _Default : System.Web.UI.Page {protected void Page_Load(object sender, EventArgs e) {if (!IsPostBack) Session["Store"] =null; }protected void Timer1_Tick(object sender, EventArgs e) { Label1.Text ="Tick at " + DateTime.Now.ToString()+"<br />";if (Session["Store"] !=null) Label1.Text +="register at-" + Session["Store"].ToString(); }protected void Button1_Click(object sender, EventArgs e) { Thread a =new Thread(Foo); a.Start(); }protected void Foo() {while (true) { Thread.Sleep(100); Session["Store"] = DateTime.Now.ToString(); } }}

It is not possible to access the session/request/response/cache from an different thread, also creating thread/using threadpool is not an good practise.


How should I update the label control then?

Or if using another thread is not good practice, i don't see how i'm going to be able to get the button postback to finish so i can update the label cleint side from the server during a (partial) postback


I am still not clear about your requirment, anyway here is the code snippet which will allow you to access the session from a different thread:

protected void Button1_Click(object sender, EventArgs e){ ThreadPool.QueueUserWorkItem(new WaitCallback(foo), Session);}private void foo(object state){ HttpSessionState session = (HttpSessionState)state; session["Store"] = DateTime.Now.ToString();}

Initial tests with this method provide the functionality I was after. I'll try to implament it today and let you know how it goes.


Indeed this seems to work well. I have 2 follow up questions relating to it though-

1. I went ahead and passed the session variable to other functions I use by reference...I'm guessing this is ok?

eg-

protected void Button1_Click(object sender, EventArgs e) { ThreadPool.QueueUserWorkItem(new WaitCallback(foo), Session); }private void foo(object state) { HttpSessionState session = (HttpSessionState)state; Thread.Sleep(100); session["Store"] = DateTime.Now.ToString(); Thread.Sleep(1000); foo2(ref session); }private void foo2(ref HttpSessionState session) { session["Store"] = DateTime.Now.ToString(); }

2. I notcied the application context still stays around after the browser is closed(presumably waiting for the thread to end?) should I manually be stopping the proccess somehow?


1. Yes you can pass it from the worker thread method.
2. The Thread returns to threadpool as soon as the foo2 method completes, no do not have to do anything.

Since it solved your problem, dont forget to mark it as answer.


Let's take another stab at this. You didn't really state the object of what you were trying to achieve. Why are you trying to use the variable temp? And if it truly is a shared variable, why didn't you just make it static? A Session variable certainly isn't global to the application. There's got to be a reason you're doing this and perhaps there is a much better approach.


The end result alows me to provided feekback to the cleint about a job currently being ran on the server (in psudo-real time). The timer updates the label on the client but it needs to be able to read from a variable (or somthing) that the job running on the server can write to. The session variable only needs to be global to the session (mutliple people could be using this at a time).

If there is another way to do this without using the cache or session I'd prefer too, but I havn't seen a way to do this yet...


Well there are certainly more expensive techniques such as using a Windows Service to host the job and querying it. Or using a DB entry to write the job status and querying it with a web service.

However you might be interested in this person's approach. He used a Cache object, but there is no reason it couldn't be Session (but Cache can be used if a unique identifier is set for a collection of jobs). The main difference is that his whole task is in this object, and it's public properties can be used to get it's status.

http://www.eggheadcafe.com/articles/20051223.asp


Use the Cache/ Store the Jobs status in DB as muliple user will be able to see the same result. BTW for one User you cannot use the session for storing the long running task status as the session access is always sequential.


I did try using the cache and i wasn't abe to get it to work ( see the first post). Essesntially the source looks the same as the session example except you use cache['whatever'] instead of session["whatever"].

I was under the impression that the session[] object was unique for each 'cleint session' that is, each cleint request would have it's own session object to work with. If this is the case why can't i use it for multiple users

@.wrayx1- I wanted to stay away from using a service althogh I have read some thing about how to get them to work, i think it overcompliactes the simple job I'm trying to do.

No comments:

Post a Comment