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