Well I stand corrected - I just looked at the code using Reflector and that’s exactly what happens :-)
Regards Richard Blewett DevelopMentor > -----Original Message----- > From: Moderated discussion of advanced .NET topics. [mailto:ADVANCED- > [EMAIL PROTECTED] On Behalf Of Dmitriy Zaslavskiy > Sent: 01 March 2004 19:18 > To: [EMAIL PROTECTED] > Subject: Re: [ADVANCED-DOTNET] Thread with message pump (UI thread) > > I think they both use PostMessage and Control.Invoke waits on event. > This difference is *almost* never makes a difference. > > Dmitriy > > Richard Blewett wrote: > > >Control.Invoke uses SendMessage internally and Control.BeginInvoke uses > >PostMessage. > > > >Regards > > > >Richard Blewett > >DevelopMentor > > > > > > > >>-----Original Message----- > >>From: Moderated discussion of advanced .NET topics. [mailto:ADVANCED- > >>[EMAIL PROTECTED] On Behalf Of John Davis > >>Sent: 01 March 2004 11:48 > >>To: [EMAIL PROTECTED] > >>Subject: Re: [ADVANCED-DOTNET] Thread with message pump (UI thread) > >> > >>Yes, Control.Invoke uses the message pump. The message pump is the > queue > >>you speak of. I'm guessing the mechanism uses a PostMessage with a > >>WM_USER > >>+ x. > >> > >>----- Original Message ----- > >>From: "John Elliot" <[EMAIL PROTECTED]> > >>To: <[EMAIL PROTECTED]> > >>Sent: Monday, March 01, 2004 5:34 AM > >>Subject: Re: [ADVANCED-DOTNET] Thread with message pump (UI thread) > >> > >> > >> > >> > >>>>If the UI never accesses the model directly (which would require > >>>> > >>>> > >>>locking > >>> > >>> > >>>>to avoid possibly getting inconsistent data if the model is in the > >>>> > >>>> > >>>process > >>> > >>> > >>>>of being updated), but only sends requests (via BeginInvoke) to the > >>>> > >>>> > >>>model > >>> > >>> > >>>>thread for information, does that solve the problem? > >>>> > >>>> > >>>I'm not sure that you can 'BeginInvoke onto the model thread'. As I > >>>understand it (please correct me if I'm wrong), if I have a delegate I > >>>can call BeginInvoke and it will be serviced by a worker thread from > the > >>>thread pool. I'm not sure that a 'worker thread from the thread pool' > is > >>>synonymous with 'model thread'. > >>> > >>>There are some gaps in my understanding around this however. For > >>>example, how is it that Control.BeginInvoke allows the delegate to find > >>>its way into the code path on the UI thread? The delegate must be added > >>>to a queue and serviced at some point right? Which queue? Does it use > >>>the message pump? If so, what sort of message does it send? Where can I > >>>read about the mechanics of Control.BeginInvoke? > >>> > >>>The way I'm trying to think about it at the moment is that I have two > >>>main threads. The 'UI thread' and the 'application thread'. The UI > >>>serves two purposes: > >>> > >>>1. Represent the model (view) > >>>2. Command the model (controller) > >>> > >>>My goal is to keep the UI as free as possible, basically the only > >>>reasons for this are so that it can paint, respond to user requests > that > >>>don't rely on the model, or to 'cancel' something that is in progress. > >>>It seems that a massive amount of work needs to be done to achieve > these > >>>relatively humble goals. > >>> > >>>The reason that this is hard in my particular case is that I have an > MDI > >>>WinForms application that shares data from a local (in-process) 'model' > >>>of the data from the database. I don't use Dataset etc. because I run > >>>into all sorts of trouble with a user fighting *with themself* over > read > >>>locks (i.e. timestamp/rowversion), defining complex relationships > >>>between data that hasn't been allocated a primary key, etc. I use my > own > >>>business objects and collections to help me manage these relationships, > >>>basically I'm building my own object-relational wrapper that is the > main > >>>component of the 'Model' in my MVC application. > >>> > >>>There are a few hairy design challenges. I want my model to be equally > >>>usable in both a web based and WinForms based application. It gets > >>>pretty challenging though, because the 'model' is designed for 'client > >>>side' representation of data (I have a stateless application server > that > >>>can service requests and commands to populate the client-side model or > >>>persist it to a data store) and these two execution contexts are quite > >>>different. > >>> > >>>In a web based app, multiple users share a process, and if two users > >>>were editing the same domain object (say, Employee, Invoice, etc.) then > >>>I'd let them do it, and fail with an optimistic concurrency error ASAP > >>>(to avoid them wasting their time). A web based app is largely > >>>request-response, and so is simple in this regard. The user takes a > read > >>>lock (in the virtual, optimistic concurrency sense) on a 'smallish' > >>>amount of data and then submits changes to that data, assuming there > has > >>>been no resource contention during the period that they were editing > the > >>>data I can just apply their changes, otherwise I can take them into a > >>>'merge' process (heh, or fail them ;). Obviously, pessimistic > >>>concurrency has a whole heap of problems (particularly with a web based > >>>system) so I choose optimistic concurrency as the lessor of two evils. > >>> > >>>But an MDI WinForms app is quite different because it offers a more > >>>sophisticated UI. In a windows based app, if I have the same domain > >>>object open in two views, and I alter one view, then I don't want: > >>>A. the other view to continue displaying stale data > >>>B. the user to have a concurrency problem when they flip to the other > >>>view later and make an edit (to the now stale data) > >>> > >>>I'd really like my business model to be resilient in the face of > >>>'disconnection' too. So a user can carve off some existing data, enter > >>>new data, specify complex relationships, and pump it all back into the > >>>live system much later. I'd like to get as much support for this into > >>>the business model (and related APIs) as possible, but this is largely > >>>beside the point at the moment. > >>> > >>>I've already dealt with a lot of these problems, and I'm pretty happy > >>>with what I've got (it's too complex for me to really go into via this > >>>e-mail). > >>> > >>>I would describe what I have client-side in my MDI WinForms application > >>>as an MVC application. I know that people argue about what MVC is, and > >>>have different ideas about it. The scope of the problem that I'm trying > >>>to describe here though, is that given this 'static' client-side model: > >>>reading from it, updating it, etc. is all (relatively) straight forward > >>>if all my processing happens synchronously on the UI thread. The > problem > >>>is that the UI thread is not free to respond to the user or paint > itself > >>>while it is processing, such processing pretty much always involves > >>>going over the wire to the application server with blocking requests > for > >>>state or commands to change state (basically the CRUD). > >>> > >>>One of the particularly challenging problems is that 'View' code often > >>>needs to do more than just 'read' from the model, often it needs to > >>>'load' data into the model, and this is a long-running process. > >>>Lazy-loading isn't an option if I don't want to block the UI thread, > and > >>>really is 'programming by side-effect', because I can affect the state > >>>of the model by doing a read operation. Design challenges are further > >>>aggravated by security, caching, etc. > >>> > >>>At the moment I'm really looking at how I can use a different kind of > >>>messaging paradigm between the Model/View/Controller than I presently > >>>have. At the moment such messages are passed using multicast delegates > >>>(AKA events). The crux of the problem with this type of notification is > >>>that the message is sent synchronously as it happens. So, I'm leaning > >>>more towards a client/server type of messaging system (more like > >>>publish/subscribe than 'observer' if it's fair to try and make such a > >>>distinction) where I can queue the multicast delegate for invocation > >>>later, rather than dispatch it immediately. > >>> > >>>What I'm finding is that I now need to maintain 'message and command > >>>queues' that are serviced on the appropriate thread. For example, if > >>>code executing on the 'application thread' causes a change in state to > >>>the model that a view has registered interest in (by attaching an event > >>>handler) then rather than synchronously invoking the event so the view > >>>can be updated immediately, I'd like to place this event on a queue so > >>>that once the processing on the 'application thread' runs to completion > >>>and the model is returned to a 'consistent' and 'not-locked' state I > can > >>>dispatch all the messages on this queue on the UI thread and let the > >>>View objects request the updated state from the appropriate parts of > the > >>>model (with no fear of the model either being locked or in an invalid > >>>state). Interestingly, it seems that I also need a queue for queuing > >>>long-running commands (like 'Save') triggered by the user (or an event) > >>>on the UI thread for processing on the application thread. Then I need > >>>some form of mutex that ensures that either the 'controller queue' is > >>>being serviced XOR the 'view event queue' is being dispatched. Meaning > >>>really that either the UI thread is reading from the model, or the > >>>application thread is updating it, but neither can attempt to run at > the > >>>same time. On the face of it, this seems like the best way to go at the > >>>moment, but perhaps this is a crazy idea? > >>> > >>> > >>> > >>>>The UI thread will update the display using information passed in > >>>> > >>>> > >>>event- > >>> > >>> > >>>>handler parameters (as you indicated was a possibility), or it can use > >>>>BeginInvoke to send a request for model state to the model thread and > >>>> > >>>> > >>>uses > >>> > >>> > >>>>the results that (eventually) come back to update the display. (The > >>>>latter could happen if the user pressed a Refresh button, or expands a > >>>>tree node whose contents only exist in the model, or some such.) If > >>>> > >>>> > >>>the > >>> > >>> > >>>>user does something in the UI that should cause an update to the > >>>> > >>>> > >>>model, > >>> > >>> > >>>>the UI thread uses BeginInvoke to pass the "update model" request. > >>>> > >>>> > >>>The 'pass state with event args' problem is interesting. Initially it > >>>seems like this might be valid, but after a while you realise that (as > >>>Shawn pointed out in his last post on this thread) you really need to > >>>'clone' state objects that are not strictly 'atomic' or 'immutable' > when > >>>you do this. Once you start adding any degree of complexity, then this > >>>can get completely out of control and you might have to clone thousands > >>>of objects just so you could send a Client.ProjectChanged event. If it > >>>was just Client.NameChanged I might be able to pass a string with the > >>>new name in the event args, but if the 'property' that changed was a > >>>related business object then I might have to clone it (and everything > >>>that it contained) just to pass the event. If the UI then wanted data > >>>from this cloned Project that had not yet been loaded, it would have to > >>>query the application server for that data. Basically my entire goal is > >>>to 'avoid cloning, at all, ever' on the client-side, because it can > >>>really hurt the working set, but more particularly because once I clone > >>>I have one user with duplicate read locks on the same data that they > can > >>>cause to become stale. My other goal is to 'avoid locking, at all, > ever' > >>>because saving myself from deadlocks is non-trivial, locking decreases > >>>performance, and I can still end up blocking the UI thread. Usually the > >>>solution to avoid one is to do the other, but I don't want to do > either. > >>>Like I said, I want to have my cake and eat it too! :) > >>> > >>>The only way that I can see to accomplish this, is to have a 'turn > >>>based' system, where the UI thread has a go at reading changes, > painting > >>>itself and queuing commands, then the application thread has a go at > >>>processing the command queue, updating the model and queuing > >>>notifications for the UI. The UI can still do 'stuff' while the > >>>application thread is executing, but it must guarantee not to touch the > >>>model. I haven't hit the point where this gets unreasonable yet, but so > >>>far it's only in my mind (no concept code or anything like that) so > >>>there may be some complexity that this introduces that is beyond my > >>>ability to address that I haven't stumbled across yet (if there is, it > >>>will most likely be with the 'controller' side of the problem, because > I > >>>haven't had a close look into that yet). > >>> > >>>The reason that I need a 'client-side' model is that I don't want > >>>'temporary state' to be maintained on behalf of clients by the > >>>application server. Say I have a scenario where I'm entering a new > >>>'Employee'. I client the 'Create new Employee' button, and up pops a > >>>form. Behind the scenes there some code specified 'new Employee();' as > >>>the data source on this form. The user enters the name etc, but wants > to > >>>add an 'Address' for the residential address. Given that the > residential > >>>address is now not going to be 'null' I create a 'new Address();' and > >>>assign it. Now I'm starting to have complex 'temporary state' in the > >>>client that will need to be persisted by the application server inside > a > >>>single transaction. If I then specified that this employees postal > >>>address 'was the same as their residential address' I might do > something > >>>like this: this.Employee.PostalAddress = > >>>this.Employee.ResidentialAddress; Now things are getting even tougher, > >>>because not only have I got complex relationships that I need to model > >>>without primary keys that will later be specified by application server > >>>(or the database) but I also have to represent this change to the user > >>>in the user interface. So, if I had two 'AddressControl' instances that > >>>represented the Residential and Postal address for this Employee on the > >>>'EmployeeControl' then if I changed the residential address I'd want to > >>>see this change reflected in the postal address control. This is a > >>>simple (and perhaps silly example) but it outlines the complexity that > >>>I'm trying to model, and should serve to demonstrate why I can't > >>>maintain the 'model' on the application server as easily as I can on > the > >>>client. > >>> > >>>Anyway, that's my rant for the day. At the end of the day the way I > look > >>>at it is that the MVC pattern (or perhaps I should say 'observer' or > the > >>>event-driven paradigm) is as old as time, async programming is as old > as > >>>time, and what I want is some good material where someone discusses > >>>methods of integrating the concepts of each. I don't really want > >>>asynchronous execution, what I really want is a method of keeping my UI > >>>responsive while I work on shared local data, it's just that async > >>>programming seems to be what I need to do to achieve this end. That's > >>>way I'm happy to simply have the 'two main threads' and just managed > >>>requests and notifications via queues. Obviously, in trying to do this, > >>>a whole lot of things need to be taken into consideration, and > >>>responsibilities and contracts need to be given to various components, > >>>I'm trying to figure out what those should be, but if someone has > >>>already done the work, I'd rather save myself some trouble.. :P > >>> > >>>Perhaps I should get myself a book on 'game programming'. Those guys > >>>have been doing stuff like this for years right? > >>> > >>>The facilities in .NET alone at the moment are not of themselves > >>>sophisticated enough to allow me to do everything that I want to do, so > >>>I really need an application framework on top of .NET to help me. > >>> > >>>Obviously there are applications that work this way. Consider how > VS.NET > >>>works. If I have a class open in the code editor and change the class's > >>>name, then the name change is reflected in the drop-down list on the > >>>top, left and in the Class View. There is a slight delay in the UI > being > >>>updated however, and my keystrokes aren't delayed while the UI updates > >>>itself (I think, I could be wrong about that), indicating that this > >>>'event' has been queued and dispatched. Basically I want to know about > a > >>>pattern for accomplishing functionality like that. Is it simply the > case > >>>that they are using WM_PAINT, and invalidating the control? If so, then > >>>what is the UI thread going to do when it wants the current value for > >>>the class name? Lock the business model of the Class so that it can > read > >>>out the name value, meaning that the code editor needs to acquire the > >>>same lock when updating? I'm sure that because of all the parsing, etc > >>>that would be required this software is *far* more sophisticated that > >>>what I'm trying to achieve, but there are elements of a similar pattern > >>>that must have been addressed here right? If I was to run a 'find and > >>>replace' operation, then I wouldn't want my code to alter the code > >>>editors text box, but rather the 'compile unit' or 'source file' > >>>business object, then have the changes reflected in the text box (the > >>>view). My system is not a text editor of course; it's basically a > >>>glorified database browser. > >>> > >>>Should I just give in and leave all my code on the UI thread? The > >>>reality is that in a 'typical' usage scenario the UI locks up for much > >>>less than 500ms during an operation, but this still sucks right? In my > >>>travels I've never seen any software adequately deal with all of these > >>>problems. > >>> > >>>John. > >>> > >>>=================================== > >>>This list is hosted by DevelopMentor® http://www.develop.com > >>>Some .NET courses you may be interested in: > >>> > >>>NEW! Guerrilla ASP.NET, 17 May 2004, in Los Angeles > >>>http://www.develop.com/courses/gaspdotnetls > >>> > >>>View archives and manage your subscription(s) at > >>> > >>> > >>http://discuss.develop.com > >> > >>=================================== > >>This list is hosted by DevelopMentor® http://www.develop.com > >>Some .NET courses you may be interested in: > >> > >>NEW! Guerrilla ASP.NET, 17 May 2004, in Los Angeles > >>http://www.develop.com/courses/gaspdotnetls > >> > >>View archives and manage your subscription(s) at > >>http://discuss.develop.com > >> > >>--- > >>Incoming mail is certified Virus Free. > >>Checked by AVG anti-virus system (http://www.grisoft.com). > >>Version: 6.0.593 / Virus Database: 376 - Release Date: 20/02/2004 > >> > >> > >> > > > >--- > >Outgoing mail is certified Virus Free. > >Checked by AVG anti-virus system (http://www.grisoft.com). > >Version: 6.0.593 / Virus Database: 376 - Release Date: 20/02/2004 > > > > > >=================================== > >This list is hosted by DevelopMentor® http://www.develop.com > >Some .NET courses you may be interested in: > > > >NEW! Guerrilla ASP.NET, 17 May 2004, in Los Angeles > >http://www.develop.com/courses/gaspdotnetls > > > >View archives and manage your subscription(s) at > http://discuss.develop.com > > > > > > > > > =================================== > This list is hosted by DevelopMentor® http://www.develop.com > Some .NET courses you may be interested in: > > NEW! Guerrilla ASP.NET, 17 May 2004, in Los Angeles > http://www.develop.com/courses/gaspdotnetls > > View archives and manage your subscription(s) at > http://discuss.develop.com > > --- > Incoming mail is certified Virus Free. > Checked by AVG anti-virus system (http://www.grisoft.com). > Version: 6.0.593 / Virus Database: 376 - Release Date: 20/02/2004 > --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.593 / Virus Database: 376 - Release Date: 20/02/2004 =================================== This list is hosted by DevelopMentor® http://www.develop.com Some .NET courses you may be interested in: NEW! Guerrilla ASP.NET, 17 May 2004, in Los Angeles http://www.develop.com/courses/gaspdotnetls View archives and manage your subscription(s) at http://discuss.develop.com