我有这个方法
public override SockResponse Process(Socket socket, Client client) {
CallResponse rsp = new CallResponse();
try
{
using (Transact X = client.Session.NewTransaction())
{
foreach (CallData call in Payload)
{
DataCall dataCall = new DataCall();
SqlDataReader rdr = dataCall.Execute(X, call);
rsp.Result.Add(dataCall.BuildResult(rdr));
rdr.Close();
}
rsp.ErrMsg = "";
X.Commit();
}
}
catch (Exception err)
{
rsp.ErrMsg = err.Message;
return rsp;
}
return rsp;
}
现在我想让它异步工作,为此我尝试使用BackgroundWorker
,新代码看起来像这样
public override SockResponse Process(Socket socket, Client client)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(
delegate(object sender, DoWorkEventArgs e)
{
CallResponse rsp = new CallResponse();
try
{
using (Transact X = client.Session.NewTransaction())
{
foreach (CallData call in Payload)
{
DataCall dataCall = new DataCall();
SqlDataReader rdr = dataCall.Execute(X, call);
rsp.Result.Add(dataCall.BuildResult(rdr));
rdr.Close();
}
rsp.ErrMsg = "";
X.Commit();
}
}
catch (Exception err)
{
rsp.ErrMsg = err.Message;
e.Result = rsp;
}
e.Result = rsp;
});
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
delegate(object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
Log.Logger.Error(e.Error.Message);
}
else if (e.Cancelled)
{
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
Log.Logger.Info("CALL process was cancelled");
}
else
{
// Then, handle the result
return e.Result; // this is where the problem is
}
});
worker.RunWorkerAsync();
}
问题是我不知道在worker
完成后如何返回结果
答案 0 :(得分:2)
您似乎希望在同步方法中运行异步代码。即你有一个返回SocketResponse
的方法,并想让它异步。
嗯,这就是问题所在。基本上 - 你不能。或者至少它不仅仅是使用BackgroundWorker
。
执行此操作的一种方法是使用TaskCompletionSource
,然后等待它完成。代码如下所示:
public override SockResponse Process(Socket socket, Client client)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(
delegate (object sender, DoWorkEventArgs e)
{
CallResponse rsp = new CallResponse();
try
{
using (Transact X = client.Session.NewTransaction())
{
foreach (CallData call in Payload)
{
DataCall dataCall = new DataCall();
SqlDataReader rdr = dataCall.Execute(X, call);
rsp.Result.Add(dataCall.BuildResult(rdr));
rdr.Close();
}
rsp.ErrMsg = "";
X.Commit();
}
}
catch (Exception err)
{
rsp.ErrMsg = err.Message;
e.Result = rsp;
}
e.Result = rsp;
});
TaskCompletionSource<SockResponse> tcs = new TaskCompletionSource<SockResponse>();
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
delegate (object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
Log.Logger.Error(e.Error.Message);
}
else if (e.Cancelled)
{
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
Log.Logger.Info("CALL process was cancelled");
}
else
{
// Then, handle the result
tcs.SetResult(e.Result);
}
});
worker.RunWorkerAsync();
return tcs.Task.Result;
}
这是一种丑陋的方法 - 您的Process
方法可能在单独的线程中运行代码,但否则需要很长时间才能执行。在这种情况下使用BackgroundWorker
也是过度的(在这种情况下,简单的Task
也会起作用)。使用Task
的代码的简化版本为:
public override SockResponse Process(Socket socket, Client client)
{
return Task.Factory.StartNew(() =>
{
CallResponse rsp = new CallResponse();
try
{
using (Transact X = client.Session.NewTransaction())
{
foreach (CallData call in Payload)
{
DataCall dataCall = new DataCall();
SqlDataReader rdr = dataCall.Execute(X, call);
rsp.Result.Add(dataCall.BuildResult(rdr));
rdr.Close();
}
rsp.ErrMsg = "";
X.Commit();
}
}
catch (Exception err)
{
rsp.ErrMsg = err.Message;
return rsp;
}
return rsp;
}).Result;
}
以上是在新线程中运行代码,但Process
方法仍然需要花费很多时间。
更好的方法是使用async/await
关键字并重写方法以返回Task
。这可能要求您在多个地方重写您的应用程序。
以下是async
方法的相同方法:
public async Task<SockResponse> ProcessAsync(Socket socket, Client client)
{
var task = Task.Factory.StartNew(() =>
{
CallResponse rsp = new CallResponse();
try
{
using (Transact X = client.Session.NewTransaction())
{
foreach (CallData call in Payload)
{
DataCall dataCall = new DataCall();
SqlDataReader rdr = dataCall.Execute(X, call);
rsp.Result.Add(dataCall.BuildResult(rdr));
rdr.Close();
}
rsp.ErrMsg = "";
X.Commit();
}
}
catch (Exception err)
{
rsp.ErrMsg = err.Message;
return rsp;
}
return rsp;
});
return await task;
}
但是,我已经看到了这方面的潜在问题:您的Process
方法是一种覆盖,这意味着您不能让它返回Task
,而且您不能只是对{ {1}}关键字就可以了。
那么,根据你运行async
方法的方式,也许你可以在一个单独的线程中运行它?有点像:
Process
或(如果不使用var socketResponse = await Task.Factory.StartNew(() => Process(socket, client));
)
async/await
另外,除非你覆盖自己的基类(意味着你可以重写代码以使其异步),所以看起来你正在使用套接字 - 默认情况下与var socketResponse = Task.Factory.StartNew(() => Process(socket, client)).Result;
使用相关的代码是,基于事件(开始/结束方法),并且应该自然地“异步”。
答案 1 :(得分:-1)
要从BackgroundWorker
返回值,请考虑遵循MSDN文章:MSDN
查看示例代码并阅读以下内容:
worker.ReportProgress(i * 10);
或者,要报告对象,请考虑以下StackOverflow文章: How to make BackgroundWorker return an object
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
int result = 2+2;
e.Result = result;
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
int result = (int)e.Result;
MessageBox.Show("Result received: " + result.ToString());
}