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

Reply via email to