今天发现在多线程中使用循环传递 HttpContext 参数时, 有时会出现传递失效的情况. 请见如下 Code
delegate DataTable GetSegDataDelegate(HttpContext context, int reportid);
DataTable dtSeg1, dtSeg2, dtSeg3;
protected void Page_Load(object sender, EventArgs e)
{
Session["company"] = "SHK";
Session["currency"] = "USD";
GetSegDataDelegate getSegDataDelegate = new GetSegDataDelegate(GetSegData);
for (int i = 7; i <= 9; i++) // CodeA
{
getSegDataDelegate.BeginInvoke(HttpContext.Current, i, new AsyncCallback(SetSegData), null);
}
while (dtSeg1 == null)
{ }
gvData1.DataSource = dtSeg1;
gvData1.DataBind();
while (dtSeg2 == null)
{ }
gvData2.DataSource = dtSeg2;
gvData2.DataBind();
while (dtSeg3 == null)
{ }
gvData3.DataSource = dtSeg3;
gvData3.DataBind();
}
public DataTable GetSegData(HttpContext context, int reportid)
{
DataTable dt = new DataTable();
try
{
int i = 0;
OracleParameter[] p = new OracleParameter[4];
p[i] = new OracleParameter("p_cursor", OracleType.Cursor);
p[i].Direction = ParameterDirection.Output;
i++;
p[i] = new OracleParameter("p_reportid", OracleType.Number);
p[i].Value = reportid;
i++;
p[i] = new OracleParameter("p_company", OracleType.VarChar, 100);
p[i].Value = context.Session["company"]; // CodeB
i++;
p[i] = new OracleParameter("p_curr", OracleType.VarChar, 50);
p[i].Value = context.Session["currency"]; // CodeC
i++;
dt = db.ExecProcedure("ACCP.ACC_SEG_PKG.query_amount", p);
}
catch (Exception ex)
{
throw ex;
}
dt.TableName = "SEG" + reportid;
return dt;
}
public void SetSegData(IAsyncResult result)
{
AsyncResult async = (AsyncResult)result;
GetSegDataDelegate DelegateInstance = (GetSegDataDelegate)async.AsyncDelegate;
DataTable dt = DelegateInstance.EndInvoke(result);
switch (dt.TableName)
{
case "SEG7":
dtSeg1 = dt;
break;
case "SEG8":
dtSeg2 = dt;
break;
case "SEG9":
dtSeg3 = dt;
break;
}
}
当 CodeA 循环到第2或第3次时 (即 i=8 或 i=9 ), CodeB, CodeC 处找不到 Session 值(即 context.Session["company"] 和 context.Session["currency"] 为 null ), 表明传入的 HttpContext 参数失效. 但第1次 (即 i=7) 是完全没有问题的. 非常奇怪?! 可改成如下 code 处理之.
delegate DataTable GetSegDataDelegate(string company, string currency, int reportid);
DataTable dtSeg1, dtSeg2, dtSeg3;
protected void Page_Load(object sender, EventArgs e)
{
Session["company"] = "SHK";
Session["currency"] = "HKD";
GetSegDataDelegate getSegDataDelegate = new GetSegDataDelegate(GetSegData);
for (int i = 7; i <= 9; i++)
{
getSegDataDelegate.BeginInvoke(Session["company"].ToString(), Session["currency"].ToString(), i, new AsyncCallback(SetSegData), null);
}
while (dtSeg1 == null)
{ }
gvData1.DataSource = dtSeg1;
gvData1.DataBind();
while (dtSeg2 == null)
{ }
gvData2.DataSource = dtSeg2;
gvData2.DataBind();
while (dtSeg3 == null)
{ }
gvData3.DataSource = dtSeg3;
gvData3.DataBind();
}
public DataTable GetSegData(string company, string currency, int reportid)
{
DataTable dt = new DataTable();
try
{
int i = 0;
OracleParameter[] p = new OracleParameter[4];
p[i] = new OracleParameter("p_cursor", OracleType.Cursor);
p[i].Direction = ParameterDirection.Output;
i++;
p[i] = new OracleParameter("p_reportid", OracleType.Number);
p[i].Value = reportid;
i++;
p[i] = new OracleParameter("p_company", OracleType.VarChar, 100);
p[i].Value = company;
i++;
p[i] = new OracleParameter("p_curr", OracleType.VarChar, 50);
p[i].Value = currency;
i++;
dt = db.ExecProcedure("ACCP.ACC_SEG_PKG1.query_amount", p);
}
catch (Exception ex)
{
throw ex;
}
dt.TableName = "SEG" + reportid;
return dt;
}
这里之所以采用多线程, 是为了提高查询速度. 之前运行的情况是 Procedure2 要等待 Procedure1 完成才运行, Procedure3 要等待 Procedure2 完成才运行. 而现在是3个 Procedure 并列同步运行, 实际运行速度几乎是原来的3倍.
另外还有一个注意点, 在线程回调过程里不可操作页面上的控件, 如下
public void SetSegData(IAsyncResult result)
{
AsyncResult async = (AsyncResult)result;
GetSegDataDelegate DelegateInstance = (GetSegDataDelegate)async.AsyncDelegate;
DataTable dt = DelegateInstance.EndInvoke(result);
switch (dt.TableName)
{
case "SEG7":
gvData1.DataSource = dt;
gvData1.DataBind();
break;
case "SEG8":
gvData2.DataSource = dt;
gvData2.DataBind();
break;
case "SEG9":
gvData3.DataSource = dt;
gvData3.DataBind();
break;
}
}