C # - исключение параллельных операций ввода-вывода - PullRequest
1 голос
/ 17 августа 2011

У меня есть кнопка обновления для обновления новостей в моем приложении WP7.Когда я дважды или трижды нажимаю на кнопку обновления, я получаю сообщение об ошибке

"WebClient does not support concurrent I/O operations" .

Я думаю, это потому, что он отправляет запрос три раза и приводит к сбою.Вот мой код клика.

    private void NewsRefresh_Click(object sender, RoutedEventArgs e)
    {
        var vm = this.DataContext as MainPageViewModel;
        if (vm != null)
        {
            vm.UpdateNews();
        }
    }

Как я могу включить его, как "если он занят, отменить процесс".

Ответы [ 2 ]

2 голосов
/ 17 августа 2011

WebClient не очень гибок, но если вы действительно хотите использовать его, вы можете использовать свойство IsBusy, а затем отменить текущую операцию.Затем, как только он отменен, вы можете перезапустить его.Есть важная проблема с синхронизацией.Операция, которая состоит из проверки IsBusy и вызова CancelAsync, не является атомарной.К счастью, DownloadStringCompleted отправляется в поток пользовательского интерфейса, поэтому вам не нужно беспокоиться о синхронизации.Фрагмент ниже показывает, как вы можете достичь этого.Для простоты это Windows Forms.

public partial class Form1 : Form
{
    WebClient _WebClient;
    bool _UpdateNews;

    public Form1()
    {
        InitializeComponent();
        _WebClient = new WebClient();
        _WebClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(_WebClient_DownloadStringCompleted);
        _UpdateNews = false;
    }

    void _WebClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        if (_UpdateNews)
        {
            _UpdateNews = false;
            UpdateNews();
        }
        else if (e.Error != null)
        {
            // Report error 
        }
        else
        {
            MessageBox.Show(e.Result);
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        if (_WebClient.IsBusy)
        {
            _WebClient.CancelAsync();
            _UpdateNews = true;
        }
        else
        {
            UpdateNews();
        }
    }

    private void UpdateNews()
    {
        _WebClient.DownloadStringAsync(new Uri("/7780525/c-isklychenie-parallelnyh-operatsii-vvoda-vyvoda"));
    }
}
1 голос
/ 17 августа 2011

Простой способ (хотя и не пуленепробиваемый):

private void NewsRefresh_Click(object sender, RoutedEventArgs e)
{
    try
    {
        NewsRefresh.Enabled = false;
        var vm = this.DataContext as MainPageViewModel;
        if (vm != null)
        {
            vm.UpdateNews();
        }
    }
    finally
    {
        NewsRefresh.Enabled = true;
    }
}

Более сложный подход потребовал бы более подробной информации о том, что именно является MainPageViewModel, и что делает UpdateNews ().По сути, вам нужно хранить значение состояния там, где вы храните экземпляр WebClient.Перед использованием WebClient вам необходимо проверить, используете ли вы его уже.Проблема возникает, когда несколько потоков могут работать в одном экземпляре или если вы выполняете несколько операций (кроме UpdateNews).Когда задействованы несколько потоков, проще всего окружить использование WebClient Mutex .

Конечно, другой вариант - не использовать экземпляр WebClient повторно, а создать новый.для каждого нового запроса.

ОБНОВЛЕНИЕ

Ну, хорошо, использование DownloadStringAsync, безусловно, принесет радость.Вышеприведенный код Отключение пользовательского интерфейса не будет работать, если вы не переместите код повторного включения.Проще всего пойти с моим последним предложением и просто создать новый экземпляр WebClient.Я не очень люблю WebClient и предпочитаю использовать WebRequest.Create .

...