zoukankan      html  css  js  c++  java
  • Silverlight 跨线程访问无效(Socket例)

    Silverlight 的许多请求基本都是异步的,用WCF也好,WEBCLIENT也好,都会进行异步请求,并提供一个事件用于执行回调。在使用WCF和WEBCLIENT与服务器通信的时候通常我们都不会遇到什么麻烦,因为WCF的类方法和WEBCLIENT都是在主线程上执行和委托事件的,理所当然,回调事件也是在主线程上运行,所以一马平川的就使用了。但是当我们用到Socket或者HttpWebRequest(HttpWebRequest我自己本身没用但是看别人在用的时候遇到相同的问题)的时候回调事件中如果试图修改UI之类的时候就会出现“跨线程访问无效的”错误异常。

    为什么呢?以Socket来说,异步的回调事件并不是在Socket对象上定义的,而是在其SocketAsyncEventArgs 对象上定义的,要实现异步,Silverlight当然会自己创建一个子线程去运行这个异步事件,所以,虽然在主线程上定义了Socket和SocketAsyncEventArgs,但是Socket执行异步方法后,将SocketAsyncEventArgs委托给了异步子线程,所以在SocketAsyncEventArgs上定义的事件回调也是子线程的,这样的话,子线程的回调函数当然就不允许去操作主线程上的对象了,因此Socket的参数回调事件上修改UI等,会引发 “跨线程访问无效的”异常。

    如何解决呢?

    Silverlight提供了一个同步上下文的类 SynchronizationContext 类(System.Treading命名空间下) ,该类提供在各种同步模型中传播同步上下文的基本功能。

    也就是说,你可以使用该类,不同线程上方法调度到某一指定线程上。

    下面用Socket为例:

    Xaml:很简单,就是一个按钮和一个文本框,按钮有一个Click事件,绑定到OnSend方法

    代码
    <UserControl x:Class="SilverlightTest.Socket1"

    xmlns
    ="http://schemas.microsoft.com/client/2007"

    xmlns:x
    ="http://schemas.microsoft.com/winfx/2006/xaml"

    Width
    ="400" Height="300">

    <Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">

    <Grid.RowDefinitions >

    <RowDefinition />

    <RowDefinition />

    </Grid.RowDefinitions>



    <TextBox x:Name="txtToSend" Grid.Row="0"/>

    <Button Grid.Row="1" Click="OnSend" Content="Send" Margin="20" />

    </Grid>

    </UserControl>

    后台代码

    代码
    1 using System;
    2
    3  using System.Collections.Generic;
    4
    5  using System.Linq;
    6
    7  using System.Net;
    8
    9  using System.Windows;
    10
    11  using System.Windows.Controls;
    12
    13  using System.Windows.Documents;
    14
    15 using System.Windows.Input;
    16
    17 using System.Windows.Media;
    18
    19 using System.Windows.Media.Animation;
    20
    21 using System.Windows.Shapes;
    22
    23 using System.Net.Sockets;
    24
    25 using System.Threading;
    26
    27 using System.Text;
    28
    29
    30 namespace SilverlightTest
    31
    32 {
    33
    34 public partial class Socket1 : UserControl
    35
    36 {
    37
    38 public Socket1()
    39
    40 {
    41
    42 InitializeComponent();
    43
    44 }
    45
    46 System.Net.Sockets.Socket socket;
    47
    48 SynchronizationContext syn;
    49
    50
    51 //发送信息按钮的单击事件
    52
    53 void OnSend(object sender, EventArgs args)
    54
    55 {
    56
    57 syn = SynchronizationContext.Current;
    58
    59 socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream,
    60
    61 ProtocolType.Tcp);
    62
    63 SocketAsyncEventArgs socketArgs = new SocketAsyncEventArgs()
    64
    65 {
    66
    67 RemoteEndPoint = new DnsEndPoint(
    68
    69 Application.Current.Host.Source.DnsSafeHost, 4502)
    70
    71 };
    72
    73 socketArgs.Completed += OnOperationCompleted;
    74
    75 socketArgs.UserToken = socket;
    76
    77 socket.ConnectAsync(socketArgs);
    78 }
    79
    80
    81 //回调函数
    82
    83 void OnOperationCompleted(object sender, SocketAsyncEventArgs e)
    84
    85 {
    86 syn.Post(GetText,"OK");
    87 }
    88
    89
    90 void GetText(object str)
    91
    92 {
    93 txtToSend.Text = str.ToString();
    94 }
    95
    96 }
    97
    98 }
    99

    为了集中说明问题,省略很很多部分,该代码的功能只是创建一个Socket和其参数,参数绑定一个完成回调事件,然后执行连接,连接好之后,引发回调OnOperationCompleted。

    如果,我们在回调函数上直接写上txtToSend.Text="XXX",一定会引发跨线程的异常,因此,我们用之前在主线程上定义的SynchronizationContext 的实例syn的Post方法,将回调子线程的操作调度到主线程上的GetText方法上,并传递个参数"OK",这样在回调函数将操作调度到GetText方法,然后GetText方法中就可以进行修改UI上的属性了。


    在前面定义 SynchronizationContext的时候,我们将它实例化为SynchronizationContext.Current,即当前线程,定义并实例化SynchronizationContext的时候是在主线程上进行的,因此 Current指的上下文或线程 就是主线程,所以在后来的回调函数中,调度GetText就是调度到主线程上,主线程可以修改UI所以GetText可以修改UI。

  • 相关阅读:
    学习笔记9
    学习笔记8
    学习笔记7
    学习笔记6
    学习笔记5
    学习笔记4
    学习笔记3
    学习笔记2
    学习笔记1
    矩形覆盖
  • 原文地址:https://www.cnblogs.com/jiewei915/p/1792958.html
Copyright © 2011-2022 走看看