希望介绍一下有关线程安全方面的知识. 我对此一直没有深入研究过,似乎资料也不多,尤其是在.net下.
Yo,
"Executes the specified delegate on the thread that owns the control's underlying window handle."
这句是说这个delegate将会在UI线程上执行,这样才可以正确控制UI。所谓UI线程,就是那个拥有被操作控件的window handle的线程。:)
To Cavingdeep:MSDN的解释我能看懂,但是我直接调用m_form的方法跟使用Invoke有什么区别呢?这才是我真正想关注的。
To idior: 多线程肯定是我接下来要关注的话题,因为做FantasyPython离不开Multithreading。
直接调就会令那个方法的执行环境(context)为work thread,显然有潜在的资源竞争危险。例子中AddString,由于只有work一个thread调用,因此没什么问题
根据Invoke的字面解释,估计会是通过post message的方式让control的owner线程调用
@FantasySoft
不是都说了嘛,对UI的操作只能在UI线程起作用,所以必须在UI线程调用。这就是为什么一定要用Invoke或BeginInvoke。你可以多写两个操作UI的例子来验证一下。
当然如果是与UI无关的逻辑那么也不必在UI线程调用。这取决于你的那个AddString方法到底做了什么。
To Cavingdeep: 我似乎有点开窍了,不过只是有点。咔咔~~~
你说的“对UI的操作只能在UI线程起作用,所以必须在UI线程调用”,这种必要性是需要自己在编程中自觉地遵循呢?还是说存在某些mechanism使得这种必要性成立呢? 我对这个感觉还是很模糊,得再找些资料来参考一下。
我倒觉得unnamed的解释挺有说服力,园子里的Rustle Liu的一篇文章Invoke 和 BeginInvoke 的真正涵义,也提到了这一点。
@FantasySoft
看来你进入了多线程与UI的混乱啊。:) 这部分实际上你不需要考虑多线程,只要你理解所有操作UI的代码不管写在哪里,都必须在UI线程上执行才能起作用,这是Windows的要求。
单线程时因为所有代码其实都在UI线程上执行所以没有任何问题,多线程时如果想在一个worker thread中操作UI的话就必须用Control.Invoke将这段代码的执行留在UI线程中,而不是你目前的这个worker thread中。如果你调用Control.Invoke那么你的worker thread将等待UI线程处理这段代码,如果是Control.BeginInvoke,也就是异步的话,你的worker thread就不会等待UI线程处理,而是立刻返回,你的代码执行会被Queue住。就是这个样子,其实很简单,你把事情想的复杂了!这里和资源争夺没有关系。
Rustle Liu在他的文章里也说的很明白了,不过题目有点不对,Control的Invoke与BeginInvoke与异步或delegate的Invoke与BeginInvoke是要区分开的。
To Cavingdeep: 那么我直接进行方法调用会有什么问题出现吗?
从理论角度分析,多线程安全只有一个需要解决的问题,就是临界资源的使用,由此衍生出当采用同步技术时可能发生的死锁,无论什么操作系统皆是如此
在win32环境中,ms建议“在一个线程环境中操作gui环境对象”,就是因为基于效率,这些操作中使用的数据大多没有使用同步来保护。知道了需要遵守这个原则的理由就好办了,一方面,对于dc等非消息驱动而直接操作的gdi对象来说,这个原则必须被遵守(或者由用户代码负责同步),否则,由于可能的资源竞争将导致不可测的后果;另一方面,对于window(包括各common control)对象,由于大多数操作采用消息驱动,即由window proc根据消息负责完成任务,用户代码一般都采用send message(或post或其他wrapper api)激活window proc而不是直接拿proc的地址去调用,系统能保证这些方式激活的window proc都在它的owner线程环境中执行
注:SendMessage的remark,摘自msdn:
If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message.
由此可知,win32 gui环境,在同一线程操作gui对象这是一个指导原则,如果用户对系统很熟悉,可以在适当情况下违背这个原则
就目前来看,.net底层仅仅是对win32api的包装(也包括其机制),因此上面的结论也适用,除非.net另外作出硬性规定
如此一来,所有问题都可以通过文自己另一个问题而得到解答,比如“那么我直接进行方法调用会有什么问题出现吗?”可以转换为“这个方法中会使用到临界资源吗(也包括这个方法间接使用的方法,当然也包括.net或win32的api)?”
btw:work thread和gui thread并没有本质区别,不过这个概念并非对系统透明,系统为gui thread维护更多的数据,基于效率,在需要使用时才申请空间,系统认为没有调用任何gui相关api的线程就是work thread,只要它调用了任何gui相关api就自动转为gui thread(无法反向转换)。实际应用中,有的work thread会有实现一个消息循环等待其他线程发来的命令,这时候逻辑上它是work thread,由于它使用了消息队列相关api(属于gui api),那么它也会被操作系统标为gui thread
你可以参看一下:
《How to embed IronPython script support in your existing app in 10 easy steps》
http://blogs.msdn.com/jmstall/archive/2005/09/01/Howto_embed_ironpython.aspx
有趣的是,这个blog最后一个回复的人也在做一个IDE,名叫FantasyPython,不知是不是你的朋友。
》So I try to accomplish one mission impossible, developping one IDE that supports IronPython. And the IDE is named FantasyPython.
地址见上一个回复。
To 王: 谢谢您的information。 那篇blog我已经看过了,而且最后回复的人就是我。呵呵~~