从主形式的线程启动时,形式冻结 [英] Form freezes when lauched from a thread in main form

查看:116
本文介绍了从主形式的线程启动时,形式冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好,我目前正在为聊天客户端和服务器编程,但是可能很难在客户端从服务器接收到聊天消息时使客户端正常工作. Atm,我发现客户端确实从服务器上获得了正确的答案,并且传入的聊天消息应该出现的表单正在加载和打开,但是当我使用myform.show();
时冻结了
我已经尝试过该委托人和调用者",但仍然冻结代码:

Hi everyone, I am at the moment programming a chat client and server, but am having a hard time making my client work probably when the client receives a chat message from the server. Atm, I''ve found out that the client does get the right answer from the server and the form where the incoming chat message should appear is loading and opening, but freezes when I use myform.show();

I''ve tried this delegate and "invoker", but it still freezes codes:

delegate void SetFormCallback(Form form);
private void Form_Changer(Form form)
{
    if (form.InvokeRequired)
    {
        SetFormCallback d = new SetFormCallback(Form_Changer);
        form.Invoke(d, new object[] { form });
    }
    else
    {
        form.Show();
    }
}



接收消息的代码是在单独的线程中运行的代码:



The code for receiving mesages is this which is running in a separate thread:

private void RecieveFromServer()
        {
//strings used for deffrense requests
            string message = "";
            string userReciever = "";
            string request = "";
            int errorResponse = 1;
            string ConResponse = "";
            while (connected == true)
            {
                srReceiver = new StreamReader(tcpServer.GetStream());
                request = srReceiver.ReadLine();
                if (request == "message")
                {
                    userReciever = srReceiver.ReadLine();
                    userName = srReceiver.ReadLine();
                    message = srReceiver.ReadLine();
//checks a global dictonary if the user that send the message already is a 
//receiver in another window(keys getting deleted from chat form on form close)
                    if (!chatWindowDict.ContainsKey(userReciever))
                    {
//main problem starts here
//chat form here is a global varible
                        chatForm = new ChatForm(chatWindowDict);
                        chatWindowDict.Add(userReciever, chatForm);
                        chatForm.userReciever = userreciever;
                        chatForm.userName = this.userName;
                        chatForm.tcpServer = this.tcpServer;
                        chatForm.formSender = this;
                        this.Form_Changer(chatForm);
                        this.add_chat_text("\n" + userReciever + " Says: \n" + message);
                    }
                    else
                    {
//this works fine because the window already is open.
                        chatForm = chatWindowDict[userReciever];
                        this.add_chat_text("\n" + userReciever + " Says: \n" + message);
                    }
                }



如前所述,每次客户端从服务器接收消息时,新的聊天表单都会冻结,并且还会对主表单进行一些有趣的操作,但是我不知道什么,我的调试器也不会说什么,聊天表单也不会有任何可能导致冻结的onload 事件.

有任何想法吗???谢谢.

-Jackie



As said every time the client recieves messages from the server, the new chat form freezes and also does some funny stuff to the main form, but I don''t know what and my debugger does not say anything, also the chat form does not have any onload events which could cause something to freeze it.

Any ideas??? Thanks.

- Jackie

推荐答案

不,这不是在UI中使用线程的方法.我什至不想考虑如果您这样做会发生什么,我只会解释如何重新设计它.

您应该将所有UI保持在单个线程中.

对UI方法,属性和构造函数的所有调用应仅在UI线程,句点内完成.

您正在一个单独的而不是UI线程中进行网络连接.这是绝对正确的.你必须.所有使用阻塞调用或执行的过程都需要花费相当多的时间,都应在单独的线程中完成.唯一的问题是在那些线程中使用UI.您不能在UI上调用任何东西,但应该与UI进行通信.为此,您需要使用System.Windows.Threading.DispatcherSystem.Windows.Forms.Control的方法InvokeBeginInvoke.

请查看我过去的答案,以详细了解其工作原理:
Control.Invoke()与Control.BeginInvoke() [ ^ ],
Treeview Scanner和MD5的问题 [来自同一客户端的多个客户端端口号 [ ^ ].

另请参阅我过去关于使用线程的答案的集合:
如何获取keydown事件在vb.net中的不同线程上操作 [启用禁用+多线程后控件事件不会触发 [ ^ ].

祝你好运,
—SA
No, this is not how to work with threads in UI. I don''t even want to think what exactly happens if you''re doing such things, I''ll just explain how to re-design it.

You should keep all the UI in a single thread as it is.

All the calls to UI methods, properties and constructors should be done only from the UI threads, period.

You''re doing networking in a separate, not UI thread. This is absolutely correct. You have to. Everything using blocking call or execution taking any considerable time should be done in a separate thread. The only problem is working with UI in those thread. You cannot call anything on UI, but you should communicate with UI. For this purpose, you need to use method Invoke or BeginInvoke of System.Windows.Threading.Dispatcher or System.Windows.Forms.Control.

Please see my past answers for detailed explanation of how it works:
Control.Invoke() vs. Control.BeginInvoke()[^],
Problem with Treeview Scanner And MD5[^].

You do just the opposite: you used Invoke in the UI thread, where it makes no sense (just a call works) and do not use it in your non-UI thread, which cannot work.

As to using threads for networking, see also my skeleton design in my past answer; it should be very close to what you want to do: Multple clients from same port Number[^].

See also the collection on my past answers on using threads:
How to get a keydown event to operate on a different thread in vb.net[^],
Control events not firing after enable disable + multithreading[^].

Good luck,
—SA


程序启动时,在确定您位于UI线程中的某个位置,可以将Dispatcher.CurrentDispatcher的值保存在一个容易到达的位置,例如一个名为_dispatcher的成员变量.

When your program starts out, at some place where you are sure you are in the UI thread, you can save the value of Dispatcher.CurrentDispatcher in an easily accessible location, e.g. a member variable named _dispatcher.

_dispatcher = Dispatcher.CurrentDispatcher;



接下来,将已识别为导致问题的代码块更改为方法:



Next, change the block of code that you have identified as caused a problem to a method:

private void AddMessageToNewForm(string userReceiver, string message)
{
  chatForm = new ChatForm(chatWindowDict);
  chatWindowDict.Add(userReciever, chatForm);
  chatForm.userReciever = userReciever;
  chatForm.userName = this.userName;
  chatForm.tcpServer = this.tcpServer;
  chatForm.formSender = this;
  this.Form_Changer(chatForm);
  this.add_chat_text("\n" + userReciever + " Says: \n" + message);
}



然后代替所有这些代码,使用Dispatcher.Invoke强制代码在UI线程中运行.



and then in place of all that code, use Dispatcher.Invoke to force the code to be run in the UI thread.

if (!chatWindowDict.ContainsKey(userReciever))
{
  _dispatcher.Invoke(DispatcherPriority.Normal,
     new Action<string, string>(AddMessageToNewForm), userReceiver, message);
}



我同意以前的响应者的观点,即在Form_Changer中使用Invoke并不是正确的进行方式.



I agree with the previous responder that the use of Invoke inside Form_Changer is not the right way to proceed.


除了UI(启动)线程之外,您无法在任何其他表单上创建表单.您还可以创建任何UI控件,或从UI线程以外的任何其他操作来实现任何控件.

要从后台线程执行此操作,必须在UI线程上调用函数才能为后台线程完成工作.

阅读 [
You cannot create forms on anything other than the UI (startup) thread. You also canot create any UI controls or manifpulate any controls from anything other than the UI thread.

To do this from a background thread, you have to Invoke functions on the UI thread to do the work for the background thread.

Read this[^].


这篇关于从主形式的线程启动时,形式冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆