昨天刚学Socket,遇到问题,至今没有整明白。请各位高手帮忙。我是用异步来处理
socket.BeginAccept(....)
socket.BeginReceive(...)
。。我在服务器端 将 多个客户端多次发送的数据 显示到一个TextBox 中时,报了一个错误:
线程间操作无效: 从不是创建控件“textBox1”的线程访问它。请大家帮我分析一下产生这个错误的原因

解决方案 »

  1.   

    这个原因天天有人问。
    在 WinForm中,每个控件创建时都会隶属于一个相应的进程,这个进程有一个ID。
    而你如果在另外一个进程中去访问它是,就会产生这个错误了。
    解决的方法大体是两个:
    1:设置
    Form.CheckForIllegalCrossThreadCalls = false;
    系统将不进行这项检查。2:使用Invoke调用委托。
    Invoke将在创建这个控件的线程中调用相应委托。
      

  2.   

    跨线程调用的代码://要使用Invoke必须要传递一个委托。所以先定义一个委托类。
    //这个委托类使用了三个参数,看后面实际初始化委托时的代码就知道每个参数的意义了。
    private delegate void DelOutput(bool isSuccess, string format, object[] args);//委托的实际执行函数
    //在某个RichTextBox上输出一行文本,并且修改它输出的颜色。这里是直接对控件RichTextBox进行操作。
    //具体的代码无需关注了
    private void directOutput(bool isSuccess, string format, object[] args)
    {
        string o = string.Format(format, args);
        //o = "【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "】" + o + "\n";
        o += "\n";
        rtbOutput.AppendText(o);    rtbOutput.Select(rtbOutput.Text.Length - o.Length, o.Length);
        if (isSuccess)
            rtbOutput.SelectionColor = Color.Green;
        else
            rtbOutput.SelectionColor = Color.Red;
    }//任何时候你想要输出数据时调用这个函数,而不是调用上面那个。
    //这个函数可以根据你的调用是否是跨线程的而进行相应的处理。
    public void Output(bool isSuccess, string format, params object[] args)
    {
        if (this.InvokeRequired)     //根据这个标志判断是否需要跨线程
        {
            //是跨线程调用,则用Invoke的方式,在创建的线程内执行。
            DelOutput indirectOutput = new DelOutput(directOutput);
            //注意这里是如何初始化参数数组并传入Invoke函数的。
            object[] ps = new object[] {
            isSuccess,format,args};        this.Invoke(indirectOutput, ps);
            return;
        }
        else
        {   
            //如果不是跨线程调用,则直接访问。
            directOutput(isSuccess, format, args);
        }
    }
      

  3.   

    上面的是使用Invoke访问,在相应控件的创建线程内调用相应函数,从而使得函数实际上不在本线程中进行。另外呢,你还可以使用BackgroundWorker之类的类进行工作,当相应的工作完成时通过事件通知主线程(创建控件的线程),在事件响应代码中也是可以直接访问控件的,属于同一线程。
      

  4.   

    对于你的情况我可以想像一下大体的操作是这样的:private delegate void ChangeText(string t);
    //这是附加一段文本的
    public void AppendText(string t)
    {
        if(textBox1.InvokeRequired)
        {
            ChangeText ct = new ChangeText(AppendText);
            this.Invoke(ct,new object[]{t});
            return;
        }
        textBox1.Text += t;
    }
    //这是完全重设文本的
    public void SetText(string t)
    {
        if(textBox1.InvokeRequired)
        {
            ChangeText ct = new ChangeText(SetText);
            this.Invoke(ct,new object[]{t});
            return;
        }
        textBox1.Text = t;
    }
      

  5.   

    phy是对的   这个问题我前天问过的
      

  6.   


    我使用
    socket.BeginReceive(..,new IAsynResult(myHandle),..) 
    在myHandle线程中处理接收到的消息。void myHandle(IAsynResult ia)
    {
      //我在这里使用 TextBox1.Text = 接收到的消息
      ????这里怎么就跨线程访问了呢?
    }
      

  7.   

    你调用一下InvokeRequired试试看!看返回值是 true还是false。另外你可以断点停下来以后看看这个线程是否和你创建TextBox的线程ID相同。
    在调试工具里可以看打开的线程ID这些。
      

  8.   

    PHY 是對的,這個應該是線程安全範疇,你去baidu搜下線程安全,應該很多。
      

  9.   

    经过调试你应该看到了吧?
    你理解的同一个线程的想法是错误的,AsyncCallback并不是你理解的那样,在主线程中执行的。而是在工作线程,也就是BeginReceive的尾部执行的。大体上来说,BeginReceive类似于这样的:
    1、创建工作线程,把AsyncCallback委托以参数或者其它形式传递给线程;
    2、线程在结束处自动调用这个委托。
    BeginReceive本身只是实现了创建线程的工作。
      

  10.   

    to:phy
    在.NET 中提供多线程处理,同步,异步操作的接口,类,方法,Attribute,都有哪些?
    我在msdn中找了一下,很迷茫
      

  11.   

    这个很多,但你不需要一个个去记的,呵呵。用的时候查查MSDN,结合一般的原则就差不多了。比如你这个BeginReceive,作为一个异步的(BeginXXX应该都是异步的)的函数,它最有可能的实现方式就是线程。
    不然,它如何与你的主线程并行呢?
      

  12.   

    msdn里面不就有说明关于线程中使用控件的安全调用和非安全调用,很详细的
      

  13.   

    WPF 和 WinForm 一样,采用 STA。MFC 也如此。其他线程访问UI需要通过事件委派的方法。比如 MFC 通过消息队列,WPF 通过 dispatcher。
    http://sunshaking.blogspot.com/2008/08/multi-threading-syncing-between-threads.html