UnauthorizedAccessException: недопустимый межпоточный доступ в приложении Silverlight (XAML / C #) - PullRequest
12 голосов
/ 06 августа 2010

Относительно новичок в C # и хотел попробовать поиграть с некоторыми сторонними API веб-сервисов с ним.

Вот код XAML

    <Grid x:Name="ContentGrid" Grid.Row="1">
        <StackPanel>
            <Button Content="Load Data" Click="Button_Click" />
            <TextBlock x:Name="TwitterPost" Text="Here I am"/>
        </StackPanel>
    </Grid>

, а вот код C #

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://api.twitter.com/1/users/show/keykoo.xml");
        request.Method = "GET";

        request.BeginGetResponse(new AsyncCallback(twitterCallback), request);
    }

    private void twitterCallback(IAsyncResult result)
    {
        HttpWebRequest request = (HttpWebRequest)result.AsyncState;
        HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
        TextReader reader = new StreamReader(response.GetResponseStream());
        string strResponse = reader.ReadToEnd();

        Console.WriteLine("I am done here");
        TwitterPost.Text = "hello there";
    }

Я предполагаю, что это вызвано тем, что обратный вызов выполняется в отдельном потоке, чем пользовательский интерфейс?Каков нормальный поток для работы с этими типами взаимодействий в C #?

Спасибо.

Ответы [ 3 ]

29 голосов
/ 06 августа 2010

Полезный способ получить простой многопоточный доступ с помощью вызова CheckAccess - заключить служебный метод в статический класс - например,

public static class UIThread
{
    private static readonly Dispatcher Dispatcher;

    static UIThread()
    {
        // Store a reference to the current Dispatcher once per application
        Dispatcher = Deployment.Current.Dispatcher;
    }

    /// <summary>
    ///   Invokes the given action on the UI thread - if the current thread is the UI thread this will just invoke the action directly on
    ///   the current thread so it can be safely called without the calling method being aware of which thread it is on.
    /// </summary>
    public static void Invoke(Action action)
    {
        if (Dispatcher.CheckAccess())
            action.Invoke();
        else
            Dispatcher.BeginInvoke(action);
    }
}

тогда вы можете обернуть любые вызовы, которые обновляют пользовательский интерфейс, где вы можете находиться в фоновом потоке, например:

private void twitterCallback(IAsyncResult result)   
{   
        HttpWebRequest request = (HttpWebRequest)result.AsyncState;   
        HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);   
        TextReader reader = new StreamReader(response.GetResponseStream());   
        string strResponse = reader.ReadToEnd();   

        UIThread.Invoke(() => TwitterPost.Text = "hello there");
}   

Таким образом, вам не нужно знать, находитесь ли вы в фоновом потоке или нет, и это позволяет избежать затрат на добавление методов в каждый элемент управления для проверки этого.

2 голосов
/ 06 августа 2010

Еще один способ сделать это - использовать функцию this.Dispatcher.CheckAccess(). Это то же самое, что вы получаете в WPF, но оно не отображается в вашем интеллектуальном значении, поэтому вы, возможно, его не видели. Что вы делаете, это проверяете, есть ли у вас доступ к потоку пользовательского интерфейса, и если вы этого не делаете, вы рекурсивно перезваниваете себе в потоке пользовательского интерфейса .

private void twitterCallback(IAsyncResult result)
{
    HttpWebRequest request = (HttpWebRequest)result.AsyncState;
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
    TextReader reader = new StreamReader(response.GetResponseStream());
    string strResponse = reader.ReadToEnd();

    Console.WriteLine("I am done here");
    ////TwitterPost.Text = "hello there";
    postMyMessage(TwitterPost.Text);
}

private void postMyMessage(string text)
{
    if (this.Dispatcher.CheckAccess())
        TwitterPost.Text = text;
    else
        this.Dispatcher.BeginInvoke(new Action<string>(postMyMessage), text);
}

Это просто очень простой пример, который может стать неуправляемым, если у вас есть несколько элементов управления. Для некоторых вещей WPF / Silverlight я использовал общую функцию, которая также принимает элемент управления UI для обновления, а также само обновление, поэтому у меня нет одной из этих функций для каждого элемента управления на странице, который может потребоваться обновить с результаты фонового потока.

1 голос
/ 06 августа 2010

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

Для отправки сообщений в поток пользовательского интерфейса вы можете использовать метод Deployment.Current.Dispatcher.BeginInvoke (делегат, объект []) .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...