2

我是 C#、RestSharp 和线程的新手,所以这就是我想要做的事情:
我制作了一个程序,允许我将照片上传到 tumblr,并且到目前为止我的上传工作正常。现在我需要停止按钮才能工作,我相信这意味着我必须使用ExecuteAsync()而不是Execute(). 我还将我的代码放入了后台工作程序中,如下所示:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    if (backgroundWorker1.CancellationPending)
    {
        e.Cancel = true;
        MessageBox.Show("You pressed Cancel.");
    }
    else
    {
    var restClient = new RestClient("http://tumblr.com/api/write");
    foreach (string item in queueBox.Items)
    {
        var request = new RestRequest(Method.POST);
        request.RequestFormat = DataFormat.Json; //I don't know if this line is necessary
        request.AddParameter("email", usernameBox.Text);
        request.AddParameter("password", passwordBox.Text);
        request.AddParameter("type", "photo");
        request.AddFile("data", FolderName + "\\" + item);
        RestResponse response = restClient.Execute(request);
        doneBox.Invoke(new UpdateTextCallback(this.UpdateText),
            new object[] { item });
    }
    }
}

我相信我已经正确设置了这个。当我按下upload它时,它会else相应地移动。但是,我认为RestResponse response = restClient.Execute(request);这是阻塞的,这不允许我的代码继续检查标志。

这就是我尝试取消它的方式。

public void stopButton_Click(object sender, EventArgs e)
{
    doneBox.Items.Add("You pressed the stop button.");
    backgroundWorker1.WorkerSupportsCancellation = true;
    backgroundWorker1.CancelAsync();
}

另外,如果这是相关的,我有:

public delegate void UpdateTextCallback(string item);这让我可以打电话UpdateTextFinishedText如上所示backgroundWorker1_DoWork



对于我的问题,我该如何ExecuteAsync在这种情况下使用?我已经搜索过,但找不到任何对我有帮助的东西,我找不到与我的代码类似的示例,而且由于我是 c# 新手,我无法将其转换为我想要的。

而且,我愿意接受建议,如果您发现我的代码效率低下或其他问题,我将很乐意接受您的建议。

谢谢你。

4

1 回答 1

3

这里有几个潜在的问题。

首先,您似乎正在尝试从后台线程访问 UI 元素(以及打开 MessageBox)。这有可能引发 CrossThread 异常*。

其次,您的代码应该看起来更像这样:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    var restClient = new RestClient("http://tumblr.com/api/write");
    foreach (string item in queueBox.Items)
    { 
        //This should be inside the foreach 
        //as it is your loop that will check for cancel. 
        //Your code is procedural once it is in the backgroundworker
        //so it would never return to the spot you had it
        if (backgroundWorker1.CancellationPending)
        {
            e.Cancel = true;
            //MessageBox.Show("You pressed Cancel.");
            //Removed this to the background worker completed method below
            //This avoids any UI cross thread exceptions
            return;
        }
        var request = new RestRequest(Method.POST);
        //I believe Json is default for Restsharp, but you would have to play with it
        request.RequestFormat = DataFormat.Json; //I don't know if this line is necessary
        request.AddParameter("email", usernameBox.Text);
        request.AddParameter("password", passwordBox.Text);
        request.AddParameter("type", "photo");
        request.AddFile("data", FolderName + "\\" + item);
        //If you just pass in item to the below Func, it will be a closure
        //Meaning, any updates in the loop will propogate into the Action
        var newItemToAvoidClosure = item;
        //To use Async, you set up the callback method via a delegate
        //An anonymous method is as good as any here
        restClient.ExecuteAsync(request, 
            response=>
            { 
                //Maybe you should do something with the response?
                //Check the status code maybe?
                doneBox.Invoke(new UpdateTextCallback(this.UpdateText),
                    new object[] { newItemToAvoidClosure });
            }
        );
    }
}

将您的后台工作人员的RunWorkerCompleted方法连接到此并在此处执行所有后处理:

private void backgroundWorker1_RunWorkerCompleted(object sender,
    RunWorkerCompletedEventArgs e)
{
    if(e.Cancelled)
        MessageBox.Show("You pressed Cancel"
}

另外,如果您使用的是 4.0+,那么我建议您查看Task Parallel Library。它可以使您的代码更清洁 IMO :)。

最后,关于上述代码的注释,将会发生的情况是后台工作人员很有可能在所有 Rest 调用完成之前返回完成。这可能会运行得相当快,并且由于无法以这种方式取消呼叫(后台工作人员已经完成),因此呼叫仍然继续进行(但我相信有一种方法可以为每个 Rest 呼叫执行此操作)。因此,在我看来,真正的问题是取消检查位于代码的错误部分(注意我将它移到循环内,以便在处理每个文件后对其进行检查)。您已经在后台线程中运行,所以在我看来,调用另一个异步是没有意义的(除非您的意图是卸载要发送的数据的循环,然后卸载实际的发送)。

所以,总而言之。我提供了调用异步的方法,但我认为更大的问题是您没有适当地检查取消调用。

*它可能不是,因为您只是访问而不是更新 UI 元素,并且您确实说过这部分正在工作(不过,它可能适用于 MessageBox)

于 2012-03-23T04:00:12.897 回答