| Author |
Share Topic Topic Search Topic Options
|
johnxjcheng
Newbie
Joined: 09-Jul-2008
Location: US
Posts: 6
|
Post Options
Quote Reply
Topic: Why GetEnitiesAsync does not behave consistently for me? Posted: 09-Jul-2008 at 8:19am |
I am building a web that uses IB layer to access a database. Most user requests involve reading data from db. So, I created a single instance of PM at the application start up and let each request thread to call the GetEnitiesAsync on this PM. All the code worked. I got the data. And the data displayed on the web pages correctly. But the whole process is erratic. Very often the GetEntitiesCompleted returns no data. And the Error property of GetEntitiesCompletedEventArgs shows this error:
"xxxAsync methods can only be called from applications running in a SynchronizationContext that supports thread synchronization. This means, in general, applications running in a WinForms or ASP.NET environment. Console or WindowsService applications must use BeginInvoke and EndInvoke mechanisms instead"
The call is from a WebForm. The application is created with VS 2008. I also verified that SynchronizationContext.Current is not null on the calling thread. So, why is the underneath call behaviour inconsistent?
|
 |
kimj
IdeaBlade
Joined: 09-May-2007
Posts: 1391
|
Post Options
Quote Reply
Posted: 09-Jul-2008 at 9:28am |
That error is thrown after checking the thread ID of both the originating request and the post-processing thread - they are expected to be the same (meaning post-processing is occurring in the same SynchronizationContext as the request).
The PersistenceManager itself is not threadsafe, so my guess is the fact that you're sharing a single PM in Application scope is what's causing the inconsistent results.
|
 |
johnxjcheng
Newbie
Joined: 09-Jul-2008
Location: US
Posts: 6
|
Post Options
Quote Reply
Posted: 09-Jul-2008 at 11:03am |
Aha, I see the problem. The WebForm page that I am using to call the GetEntitiesAsyn is an asynchronous page. According the Microsoft document ( http://msdn.microsoft.com/en-us/magazine/cc163725.aspx) this page will be attached to different threads before and after the call to the asynchronous external function. This is the only reason that I can think of why your library sees two different thread ID. If this is the case, it means GetEnitiesAsyn is not suitable for the asynchronous page then?
|
 |
johnxjcheng
Newbie
Joined: 09-Jul-2008
Location: US
Posts: 6
|
Post Options
Quote Reply
Posted: 09-Jul-2008 at 11:16am |
Kimj, I understand that PersistenceManager is not thread safe. That’s why I am calling GetEnititiesAsync. From reading the DevForce document “Multi-Threading in a DevForce App”, I get the impression that GetEntitiesAsync is the answer to share a PM in multi-threaded environment.
|
 |
kimj
IdeaBlade
Joined: 09-May-2007
Posts: 1391
|
Post Options
Quote Reply
Posted: 09-Jul-2008 at 12:09pm |
GetEntitiesAsync is more a means of making non-blocking data requests than of sharing a PM. When GetEntitiesAsync returns it always tries to merge its results into the PM cache, and requires that this merge occur on the same thread that made the request (and presumably the same thread that created the PM). The PM cache does not intrinsically support any type of multi-threaded access - either read or write.
I'm not an ASP.NET person, but based on the article I'd say that async pages cannot be used with GetEntitiesAsync calls. GetEntitiesAsync also uses the thread pool when it goes async, which also sounds less desirable here too. You might be able to create a delegate for the GetEntities method you're using, and call BeginInvoke and EndInvoke on that. This would bypass our checks, but if you synchronize access to the PM when necessary it may work.
I'll see if our ASP.NET guy can provide any more insight
|
 |
johnxjcheng
Newbie
Joined: 09-Jul-2008
Location: US
Posts: 6
|
Post Options
Quote Reply
Posted: 09-Jul-2008 at 1:06pm |
Kimj,
So, one way is to not sharing the PM and make synchronous calls to it. I implemented that as my first attempt. But when I saw the nature of my request processes had a large number and short life, I started to worry about the efficiency of creating new PMs every time. I am not sure how heavy the PM object is, and how intensive it is to connect and disconnect. With your internal knowledge, would you recommend using it that way?
Another way is to create a wrapper around the PM, let’s call it MyPM. MyPM should have a delegate called MyGet. MyGet can be called asynchronously from the asynchronous pages. But what I do inside this function? Call your GetEnities synchronously? I will need to implement locking to prevent the concurrency. This would be a bottleneck. Call you GetEntitiesAsync? I still need to block myself to wait for the result, which is also bottleneck.
Maybe I also need to implement MyGet as a asynchronous call and implement a MyGetComplete event. I implemented this idea once. I also got the same exception I stated earlier. Maybe I didn’t implement it completely right. Within DevForce or your customers, has anybody else encountered the same issue?
|
 |
kimj
IdeaBlade
Joined: 09-May-2007
Posts: 1391
|
Post Options
Quote Reply
Posted: 09-Jul-2008 at 2:13pm |
There is some initialization overhead when constructing a PM, especially the first time when some statics are created, but in order to ensure reliable results it may be your best alternative. Connect() logic is quite fast if you are not using a BOS, and even if you are using a BOS, Connect() calls after the first are usually fast. Login() logic may actually be a bit slower, esp. if you've implemented a LoginManager, and a Login is always implicitly done when needed prior to any fetch.
In our web service implementation support we create (and discard) a PM per request and haven't seen any issues. You can also store the PM in Session scope.
If you wrap the PM in order to make your own async calls, I think I would use the synchronous GetEntities call and allow the worker thread to block until completion. You would need to provide your own locking to synchronize access to the PM. Another option would be to use a "main" PM and one or more "worker" PMs, and then use ImportEntities to merge results into the main PM.
|
 |
johnxjcheng
Newbie
Joined: 09-Jul-2008
Location: US
Posts: 6
|
Post Options
Quote Reply
Posted: 09-Jul-2008 at 2:24pm |
|
If create a PM per request or per session, it there a limit on the number of PMs that can be opened concurrently?
|
 |
kimj
IdeaBlade
Joined: 09-May-2007
Posts: 1391
|
Post Options
Quote Reply
Posted: 09-Jul-2008 at 2:28pm |
|
No, there's no set limit on the number of created / connected / logged in PMs.
|
 |
johnxjcheng
Newbie
Joined: 09-Jul-2008
Location: US
Posts: 6
|
Post Options
Quote Reply
Posted: 09-Jul-2008 at 2:38pm |
|
Ok, I think I will take the simplest one PM per session solution for now. Thanks for your information!
|
 |