.NET Forum / .NET Framework / New Users / August 2005
Possible improvements to Asyncronous Pattern?
|
|
Thread rating:  |
sb534dan@hotmail.com - 29 Jul 2005 14:35 GMT I've always found it beneficial that win form component events always callback into the UI thread (as oppose to a worker thread). The UI client code is never exposed to the worker thread. I find this very desirable, as this prevents resource sharing problems from within the callback method. This guarantees that all of the client code runs within its own thread.
With the upcoming release of .NET 2.0, various classes have been introduced to aid in the development and usage of components providing asynchronous operations. This is great for components (classes implementing IComponent or deriving from System.Component). But for normal (non component) classes, this functionality isn't provided.
When implementing the traditional .NET async pattern for normal classes ( http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cp conasynchronousdesignpatternoverview.asp ), the callback is always run in a worker thread context. Therefore, exposing the client code to a different thread.
I've considered implementing similar functionality to the .NET 2.0 component async functionality, but for normal classes. To do this. A queue would have to be introduced for every thread in a program. (Something akin to a traditional windows message queue).
This, I feel would eliminate the need for my class to expose its worker thread to the client.
I'd love to hear any comments on this. My reasoning and potential implementation maybe flawed. But I really would like an overall opinion.
Dan.
David Browne - 29 Jul 2005 16:02 GMT > I've always found it beneficial that win form component events always > callback into the UI thread (as oppose to a worker thread). The UI [quoted text clipped - 26 lines] > implementation maybe flawed. But I really would like an overall > opinion. If you're not in a UI context, why do you want the callback to occur on the calling thread?
David
sb534dan@hotmail.com - 29 Jul 2005 16:14 GMT > If you're not in a UI context, why do you want the callback to occur on the > calling thread? > > David If it doesnt callback on the calling thread, then the client is able to "access data at the same time", as the code in the callback. i.e. the client code is exposed to two threads (instead of just the one, in the case of win forms).
David Browne - 29 Jul 2005 16:35 GMT >> If you're not in a UI context, why do you want the callback to occur on >> the [quoted text clipped - 6 lines] > client code is exposed to two threads (instead of just the one, in the > case of win forms). But if you don't want simultaneous access, why are you using a background thread at all?
And what is the calling thread doing that can be interrupted to process a callback? And what is the mechanism for interupting the calling thread?
If the calling thread becomes idle and wants to wait for the background operation to complete to complete, it can just call IAsyncResponse.WaitHandle.WaitOne().
David
Lloyd Dupont - 29 Jul 2005 17:02 GMT I believe you (I mean sb534dan, the original poster) don't fully understand what's a thread. maybe you should investigate the matter further? there is nothing really complex with them....
 Signature There are 10 kinds of people in this world. Those who understand binary and those who don't.
> >>> If you're not in a UI context, why do you want the callback to occur on [quoted text clipped - 19 lines] > > David sb534dan@hotmail.com - 29 Jul 2005 17:30 GMT I think perhaps im not explaining myself clearly enough. I would like the caling thread to say the following: "Start reading from this socket (but dont block). Inform me once you've read some data by calling this callback. I require that this callback only be called, when im not processing anything (my thread is idle)".
Something like this (very rough psuedo code):
class Socket { // Begins reading. (doesnt block). public void BeginReading(Delegate callback) { // Starts thread to process read, and returns straight after. // When worker thread completes work. It will queue the correct delegate to the calling threads queue. } }
class Client { Socket _socket = new Socket();
Queue _queue = new Queue();
// Constructor public Client() { // Init the (thread-safe) queue for this thread (holds an array of delegates). _queue = new Queue();
// Init socket _socket = new Socket();
// Start reading from socket (non-blocking). _socket.BeginReading(new Delegate(OnReadCompleted))
// Process main application loop MainLoop(); }
public void MainLoop() { // Loop forever (note that exit functionality is not shown) while ( _queue.WaitOne()) { Delegate handler = _queue.Dequeue(); // Call the handler (i.e. OnReadCompleted) handler(); } }
// This is called on the client thread!...not on a worker thread. // the client can access any of its member data and not have to worry // ...if this were called *whenever*, the client maybe in another one of its methods, accessing the shared data. public void OnReadCompleted() { } }
Lloyd Dupont - 30 Jul 2005 02:04 GMT 1st you don't call into a thread. a thread is either terminated or on a handle.Wait() instruction in the middle of a function.
the GUI thread is a special thread, it's looping! (infinitely) and it does accept new event too be processed (through an invisible queue). if your thread should behave like that why don't you use the MessageThread I write some pseudo code about earlier in this thread?
2ndly, you work with either of the above mentionned thread and you want to "post" your delegate call into their queue. you'll have to use 2 method! the AsyncDelegate, which would post the message to this (special) thread's queue in C# 2.0 it could be onde in one line myStream.BeginRead(buffer, offset, count, delegate(IAsyncResult ar){ myEvThr.Dispatch(myStream, ar); }, myStream);
where myEvThre is the MessageThread I wrote about earlier, in an other answer to your questions.
 Signature There are 10 kinds of people in this world. Those who understand binary and those who don't.
>I think perhaps im not explaining myself clearly enough. I would like > the caling thread to say the following: [quoted text clipped - 58 lines] > } > } sb534dan@hotmail.com - 30 Jul 2005 12:01 GMT > 1st you don't call into a thread. > a thread is either terminated or on a handle.Wait() instruction in the > middle of a function. Yes. strictly you are correct. I should have wrote "I would like the calling CODE to say the following...etc"
> the GUI thread is a special thread, it's looping! (infinitely) and it does > accept new event too be processed (through an invisible queue). > if your thread should behave like that why don't you use the MessageThread I > write some pseudo code about earlier in this thread? I only saw your pseudo code, after posting my own. I wouldnt have bothered If I had. Both implementations show the concept im trying to explain.
> 2ndly, you work with either of the above mentionned thread and you want to > "post" your delegate call into their queue. [quoted text clipped - 7 lines] > where myEvThre is the MessageThread I wrote about earlier, in an other > answer to your questions. Thanks for your implementation, and your tip about the up-coming c# code. However, implementation specifics is not what i posted about.
Its the concept I wanted to talk about. As you correctly mentioned, its a main application thread which bahaves like the windows GUI thread (fed by messages).
...can anyone else see the advantage of async call-backs occuring on the client thread?....would the following be true?:
IF:
1. An entire (client) application could be written based open the above one thread (application running in a big loop that re-acts to events) AND
2. Whenever the application wished to perform in asynronous operation, it could do so, calling a class which supported those operations making use of the said architecture.
THEN...
The application writer, would *never* have to worry about threading issues, because ALL of the application code, he has written, runs in ONE thread.
Note, that any threading complexity is in the classes providing the ayncronous operations. The client doesnt have to worry.
Lloyd Dupont - 30 Jul 2005 13:27 GMT I hardly see your point, to tell the truth....
After much reading and reading of your post I think what you believe is the following:
You believe that if AsuncCallback could be made to callback in the main thread your application would be thread safe by default and you won't have to worry, is that right?
Let me tell you: it's complete rubbish!
If you call (in the main thread) you stream while it's performing it's async read/write, it will get corrupted. End of discussion. No matter what kind of 'callback architecture' you've got.
Lloyd Dupont - 30 Jul 2005 13:30 GMT oops... pressed CTRL+ENTER accidentally...
Anyway, on the other hand, if this other thread bother you, dont use AsyncCallback but
IAsyncResult ar = myStream.BeginRead(....);
// somethings.......
check completion if(ar.IsCompleted) doSomething()
so it's all in the main thread, you see! but it's not thread safe at all!
you don't write it explicitely, but certainly you understand that the read method is currently executing in an other thread, don't you? don't perform other operation on the stream!
sb534dan@hotmail.com - 31 Jul 2005 21:02 GMT Yes of course the stream can be corrupted. Its instance members are not thread-safe. But this architecture stops the client corrupting its *OWN* data.
// Example 1 // Normal class.
Class Application { List<int> _intArray = new List<int>() = { 0, 1, 2, 3, 4 };
Stream _stream = new Stream();
public void Main() { _stream.BeginRead(StreamCallback, ..., .etc)
// call back may occur before during this...causing // client data corruption. for (int i = 0; i < _intArray.Length(); i++) { Console.WriteLine(_intArray[i]); } }
private void StreamCallback() { // Remove all ints.
// Because this can occur at anytime // i.e. in a stream worker thread... // a client may corrupt its own data.
while (_intArray.Length() > 0) { _intArray.RemoveAt(0); }
} }
// Example 2. // Same class as above using callback architecture.
class Application : CallBackArchitecture { List<int> _intArray = new List<int>() = { 0, 1, 2, 3, 4 };
Stream _stream = new Stream();
public void Main() { _stream.BeginRead(StreamCallback, ..., .etc)
// Can do this and not worry. // Callbacks will occur on the same thread as this one. for (int i = 0; i < _intArray.Length(); i++) { Console.WriteLine(_intArray[i]); } }
private void StreamCallback() { // Remove all ints.
// This occurs on the clients thread. // No possiblity of client data corruption.
while (_intArray.Length() > 0) { _intArray.RemoveAt(0); } } }
Lloyd Dupont - 29 Jul 2005 16:57 GMT What you need is not a queue but a "message thread" class, waiting for message and dispatching them. I don't think it needs to be an API class as it is: 1. simple enough to program 2. really depends on the specific of the application
about 2, I would say that myControl.BeginInvoke(aDelegate) is a GUI specific mechanism, which work well with the GUI event thread.
But how is it to work with any other thread? I mean, common, a thread is just a unit of code execution, it doesn't have concept such as 'event' or 'message'
Anyway here is a pseudo code MessageThread implementation for you (from the top of my head).
class MessageThread<T> { Thread runner; LinkedList<MsgInfo> list = new LinkedList<MsgInfo>(); ManualResetEvent waiter; class MsgInfo { public MsgInfo(EventHandler<T> eh, object sender, EventArgs<T> e) { this.eh = eh; this.sender = sender; this.args = e; } public EventHandler<T> eh; public object sender; public EventArgs<T> arg; }
public MessageThread() {
runner = new Thread(MessageLoop); waiter = new ManualResetEvent(false); }
public void Run() { runner.Run(); } void MessageLoop() { while(running) { waiter.Wait(); MsgInfo msg = null; lock(list) { if(list.Count == 0 || !running) waiter.Reset(); else msg = list.RemoveHead(); } if(msg != null) msg.eh(msg.sender, msg.args); } }
public void Dispatch(EventHandler<T> eh, object sender, EventArgs<T> e) { lock(list) { list.AddTail(new MsgInfo(eh, sender, e)); waiter.Set(); } } }
 Signature There are 10 kinds of people in this world. Those who understand binary and those who don't.
> I've always found it beneficial that win form component events always > callback into the UI thread (as oppose to a worker thread). The UI [quoted text clipped - 28 lines] > > Dan. Brian Gideon - 01 Aug 2005 15:01 GMT Dan,
You could use the same approach that Microsoft used when designing the System.Timers.Timer class. That class has a property called SynchronizingObject that accepts an ISynchronizeInvoke interface. When the timer has a synchronizing object set it invokes the callback on the thread hosting that object, otherwise it invokes the callback from the thread pool. Forms and Controls implement ISynchronizingInvoke so the pattern works well for what you're after.
Brian
> I've always found it beneficial that win form component events always > callback into the UI thread (as oppose to a worker thread). The UI [quoted text clipped - 28 lines] > > Dan. sb534dan@hotmail.com - 02 Aug 2005 18:42 GMT Thankyou Brian,
Just the sort of feedback i was looking for. I'lll look into ISynchronizeInvoke. Cheers.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|