From: Peter Duniho on
Mihajlo Cvetanović wrote:
> On 6/3/2010 5:25 PM, Peter Duniho wrote:
>> Hector Santos wrote:
>>> [...] The fact is you
>>> got a BUG and you should be MAN enough to admit.
>> You can claim there's a bug until you're blue in the face, it still
>> won't make it true. You have not, and cannot, demonstrate the code I
>> posted breaking in the context in which I specifically stated it was
>> intended to be used.
>
> Hector believes this would be a demonstration: in the first iteration of
> the main loop first thread is created, its function set, and spawned to
> run. BUT, when its time for second iteration first thread might already
> be finished. This in turn decrements count, which reduces the number of
> threads that would be spawned.

In the wrong context, this is indeed a potential outcome. That's why I
was so specific about the context in which the code I posted is intended
to be used.

> In C++ world this would be the case because RunWorkerCompleted would run
> from spawned thread's context, but in C# world it will be run from main
> thread's context, AFTER all threads are already spawned.

It's not so much a language-dependent behavior, but a run-time
context-dependent behavior. In particular, as I've pointed out
previously and as Adam's reiterated, the important thing is whether
there is a way for BackgroundWorker to do what it's designed to do (i.e.
whether there's a SynchronizationContext from the Forms or WPF APIs for
the thread in which the BackgroundWorker instance was created).

Assuming one is writing managed code, then it does not matter whether
the code is written in C++ or C#; as long as the BackgroundWorker is
created in the GUI thread for a Forms or WPF application, the code I
posted works fine.

If one is not writing managed code, then all of this is moot because the
code I posted won't compile.

> So, really, there's no bug here, AFAICS.

That is correct. There's no bug here. Thank you for your independent
confirmation of that!

Pete
From: Adam Clauss on
On 6/3/2010 10:35 PM, Peter Duniho wrote:
> It's useful to note that BackgroundWorker will work in _any_ thread
> where there's a marshaling SynchronizationContext. That is, the
> default SynchronizationContext doesn't actually marshal delegate
> invocations from one thread to another, but the SynchronizationContext
> found in a thread where Application.Run is running, or a WPF
> dispatcher is running, will do that.
>
> So by default, BackgroundWorker provides the full functionality of its
> features in either a Forms or WPF application, provided it's created
> in a GUI thread in that application. Furthermore, it is possible to
> write one's own SynchronizationContext class that works in other
> contexts. The important thing is that a SynchronizationContext is
> available for BackgroundWorker to use that will marshal delegate
> invocations onto the thread that created the BackgroundWorker. It
> doesn't have to be a Forms, or even WPF, application.

As a side note - thank you for this little explanation... I've had more
than a couple cases come up where I was dealing with a 3rd party API
which, by their requirement, I could only create it's objects and make
calls on those objects from a single thread (no UI even involved in
these situations).

I ended up creating my own object which I closely related to a
"ThreadPool" that only had 1 thread in it processing items (I did not
want to make use / alter the system thread pool). I have no idea what
would have been involved in writing "one's own SynchronizationContext",
but I'm wondering if that's basically what I did, or if trying to do so
would have simplified anything...

Anyway, gave me something to go think about and read up more on.

Thanks,
Adam
From: Peter Duniho on
Adam Clauss wrote:
> As a side note - thank you for this little explanation... I've had more
> than a couple cases come up where I was dealing with a 3rd party API
> which, by their requirement, I could only create it's objects and make
> calls on those objects from a single thread (no UI even involved in
> these situations).
>
> I ended up creating my own object which I closely related to a
> "ThreadPool" that only had 1 thread in it processing items (I did not
> want to make use / alter the system thread pool). I have no idea what
> would have been involved in writing "one's own SynchronizationContext",
> but I'm wondering if that's basically what I did, or if trying to do so
> would have simplified anything...

Yes, the innards of a custom SynchronizationContext is likely to look a
lot like a thread pool with one thread.

The Forms version actually uses the thread's message queue, and I don't
actually know how the WPF one works. But those are special cases meant
to deal with other requirements those APIs have. A custom one usually
wouldn't be bound by those issues and could just use a regular work
queue with a single consuming thread.

> Anyway, gave me something to go think about and read up more on.

I recall a discussion with someone else several months ago (maybe even a
year or more…I don't recall for sure), in which they wanted to write
their own SynchronizationContext.

It should not be _too_ involved, and if I recall correctly, they were
able to get theirs working. I think they might have even posted the
source code here.

If you're interested and if after looking you aren't able to find the
thread I'm talking about, follow up here and I'll take a look myself.

Pete