【问题标题】:ExecuteAsync RestSharp to allow backgroundWorker CancellationPending c#ExecuteAsync RestSharp 允许 backgroundWorker CancellationPending c#
【发布时间】:2024-01-23 19:35:01
【问题描述】:

我是 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# 新手,我无法将其转换为我想要的。

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

谢谢。

【问题讨论】:

  • 您在 DoWork 的开始看到取消的几率为零。在循环内移动测试。可能已经足够好了。

标签: c# backgroundworker blocking kill restsharp


【解决方案1】:

这里有几个潜在的问题。

首先,您似乎正在尝试从后台线程访问 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)

【讨论】:

  • 谢谢,当我尝试使用此代码时,我会收到A local variable named response cannot be declared in this scope because it would give a different meaning to 'response' which is already used in a 'parent' or curren't scope to denote something else. 如果我将其更改为`RestResponse _response = restClient.ExecuteAsync(request,`它会删除此错误,但现在我会收到@987654326 @
  • @Anteara 我已经更新了我的答案以删除存储异步调用的响应(因为你在这里真的不需要它)。此外,我还添加了使用 RunWorkerCompleted 来更好地处理潜在 UI 处理错误的建议。最后,您可以将客户端的超时设置为更高的值(我不确定默认值)以解决您当前的超时问题。但是,这似乎解决了您最初的问题。如果这是真的,那么我们总是会感谢您的支持和接受的答案:)
  • 谢谢,感谢您的帮助。我搜索了源代码并通过以下方式使其正常工作:byte[] byteArray = File.ReadAllBytes(FolderName + "\\" + item);request.AddFile("data", byteArray, FolderName + "\\" + item); 老实说,我不知道为什么会修复它,但确实可以:D。
  • @Anteara Hrmm,刚刚查看了代码,它看起来应该可以工作...进一步查看:)
  • 我猜它可能与 StreamReader 有关?绝对有趣。但是,您的工作代码以不同的方式获取数据......这可以解释为什么一个有效而另一个无效