完成后C#BackgroundWorker返回值

时间:2016-10-26 08:23:06

标签: c# multithreading asynchronous backgroundworker

我有这个方法

 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完成后如何返回结果

2 个答案:

答案 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());
}