ExecuteAsync RestSharp для разрешения отмены backgroundWorkerPending c # - PullRequest
2 голосов
/ 23 марта 2012

Я новичок в 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);, что позволяет мне звонить UpdateText и FinishedText, как показано выше в backgroundWorker1_DoWork.



На мой вопрос, как я могу использовать ExecuteAsync в этом контексте?Я искал, но не могу найти ничего, что мне поможет, я не могу найти пример, похожий на мой код, и, поскольку я новичок в c #, я не могу преобразовать его в то, что я хочу.

А также, я открыт для предложений, если вы заметите некоторую неэффективность в моем коде или что-то еще, я буду рад принять ваши предложения.

Спасибо.

1 Ответ

3 голосов
/ 23 марта 2012

Здесь есть несколько потенциальных проблем.

Во-первых, вы, кажется, пытаетесь получить доступ к элементу пользовательского интерфейса из фонового потока (а также открыть 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). Таким образом, мне кажется, что реальная проблема заключалась в том, что проверка отмены была в неправильной части кода (обратите внимание, что я переместил его в цикл, чтобы его можно было проверить после обработки каждого файла). Вы уже работаете в фоновом потоке, поэтому мне кажется, что нет смысла вызывать другой асинхронный режим (если только вы не намерены разгрузить цикл данных, подлежащих отправке, что затем разгрузит фактическую отправку).

Итак, в заключение. Я предоставил способ вызвать асинхронный вызов, но я считаю, что большая проблема заключалась в том, что вы не проверяли вызов отмены надлежащим образом.

* Это может быть не так, поскольку вы только получаете доступ и не обновляете элемент пользовательского интерфейса, и вы сказали, что эта часть работает (хотя, вероятно, это будет работать с MessageBox)

...