[Autogenerated] I've created another project in the
same solution called primes Pool.
The purpose of this project is to perform the same kind of
operations we did in the previous example,
but using the thread pool instead of creating threads explicitly.
So the main function is almost the same.
The only difference is that we're going to accept number here,
which are going to call chunks and not threats because we're
not going to create threats explicitly.
We're going to tell the thread pool hey,
thread pool, please run this function on some other thread.
I don't really care which thread that is, but it's not the current threat.
So it's up to the thread pool to create or destroy threats as
necessary or as it seems or deems needed.
And so we're going to call that chunks in order to represent a number of chunks.
We're going to chop up a range of numbers.
The rest of the function is the same as the previous one,
just calling getty count 64 before and after the operation
is complete and displaying the results.
The first change we see here is in the cult primes function.
Since we're not creating our own threads.
How can we get a handle to these threads in some way so
we can wait for them to complete.
We can't do that in a very efficient way because,
well,
it's not a simple handle for every threat because really we don't
know how many threads there are and we might have many more items
thrown at the thread pool then they're actually threat being created
for those items to be processed.
So we have to use some different mechanism and what I'm doing here?
I'm using an event object event object is the synchronization primitive,
which is just like a flag could be down,
could be up,
so the weight is going to be on this event object
which starts in the non signal state, that's the false here.
And then we're going to just wait until it's going to be signaled.
And essentially signaling means that the entire set
operations complete at least.
That's from the perspective of the main threat,
we'll see in a moment how that actually translates to code So at
this point we need to perform the same kind of calculations
we've done in the previous example,
we have the size of the chunk based on the total numbers we have to process,
we're going to allocate a bunch of data structures called red data again,
but the content of that data is now different.
Again,
this is unfortunately necessary because of the
slight loss of control we have here,
we don't have handles to threats we create because we're
not creating these threats ourselves.
So we have to manage data in slightly a different manner.
And the problem is how to know when we're done,
how to know when we're done since we don't have handles to the threats,
we can't wait on them.
So how do we know that we're done?
So the threat data structure looks like this.
It has the first and last and this is the only thing that is the same as
in the previous code but now we also have pointers to the total number of
numbers that have to be processed and the pointed to the actual number of
primes we have calculated so far.
Why do we have pointers here?
That's because they're going to be pointing to global or
shared variables and every time a piece of code will be
done with a bunch of numbers, it will document the total Hero,
the referencing that total and when it reaches zero,
that would be a good time to signal the event object and to let
the main thread know that we're completely done.
So this is have to be shared between all the processing
code that is happening at the same time by multiple
threats created by the thread pool.
Primes is going to be the shared number where we need to kind of
again add a number indicating how many primes we have found from
our little piece of code in our thread currently running on one of
the threads of the thread pool.
So we have to use a shared data here and share data implies
synchronization requirements because if we don't share,
we might not get the correct results and I'll also show you that as well.
This prime function is the same.
So what count primes does um so go back to going back to cult crimes for a
moment for every item we create the first and last are specified in the same way
as we did in the previous example total is going to point to this total number
that's going to be documented by each piece of Corentin which is zero and then
the event would be signaled will see that code momentarily and then the actual
account is going to be dis shared variable that's going to be implemented by the
pieces of code that provides the calculation and and and so the count will
eventually have the final result and the event is sent to every one of these
chunks because only one of them will have to call set event but we don't know
which one will be the one that actually finishes the last um,
piece of or last set of numbers.
So we provide all of them and access access to this age
event to be handled to the event.
And then we call try submit thread pool called back,
here's our function called count primes.
That's the function we need to call for every threat that
the thread pool allows us to to use.
So that's kind of the replacement for the count crimes that was our thread
function in the previous example and there's the data right here.
There's some other custom options we can specify here
including the dreadful to use and so on,
which is beyond the scope of what we can cover in this course and we provide,
provide that do that in loop and just wait for everything to be over.
Once it's over.
We can return in fact the accounts,
we need to return the count here of course which I uh made some slight
changes that we have to just return the count here which was hopefully
accumulated by the various pieces of code.
The last piece of the puzzle is the count crimes function that's going to be
invoked by the thread pool for each one of these chunks.
So what we need to do here is to grab the list of numbers and
calculate the prime numbers that we find in that same way as we
did in the previous example however,
we now have to update the shared information first.
We have to add to the primes pointer to the integrate points to
really the count of prime numbers we have now found.
So that would be the first shared variable notice using a
function here called Interlocked AD.
And this is instead of writing something like this that's grabbing
like data primes and doing something like this.
Simple plus equals.
It might seem like a good idea but in fact this is a data
race because it might be changed by multiple threats at the
same time and we can't allow that.
So, interrupt.
Ad is what's known as a compiler intrinsic which is really some
very special instruction to the processes to perform this
operation in atomic fashion atomic manner.
Nobody can see the result in between while things perhaps are changing.
They can see everything that might be there before the
operation or after the operation, but nothing in between.
And the next thing to do is to say,
well we have actually taken care of this number of numbers so we need
to detriment that or subtract that from the total were being handed and
again we have to do that in a safe way.
But using interrupt ed again.
And because we're actually subtracting here because of that leading minus,
we need to check whether the result is zero.
If it is, it means that we are the last threat to end the calculation.
We need to set the event.
Otherwise the weight in the main thread will wait really indefinitely.
And that's it.
That's at least one way of doing that.
Let's test it out.
Let's see how it performs compared to the previous case.
And so here's the some of the results we got from the normal primes case,
starting with 8.1 seconds roughly.
We can do that now with the primes uh pool here to see what that looks like.
Let's start with one to get a base reading here using a single thread
but it is a threat coming from the thread pool.
We don't really expect to find any big differences here
because most of the work is really about number crunching
and less about threading here.
And so we got a higher number, but again,
these things are kind of thick a little bit because other
things are happening on the machine.
So if I go back to the first example, I might get a similar number,
maybe slightly different, and you can see it's actually the same thing.
So if we go ahead and start adding threats or really adding chunks,
we're not really concerned with threats here,
it's up to the thread pool um to use as many threads as needed or
at least as many threads as it seems to need.
So here we can see again numbers fairly similar,
let's increase that further and let's go with eight threads
and if we compare that to the previous case,
the numbers are are fairly similar, it might be slightly higher because again,
of fluctuations within the system itself,
so it's not really super accurate here,
but we can increase that number definitely just like
we did with the previous case,
but now the main difference is that the number of threats will not
grow will not grow in an uncontrollable fashion because the thread
pool works in the following way.
If there's lots of requests coming into the thread pool,
it will create more threats,
but we will do so in uh in steps roughly one thread per second.
And because it assumes that other threats will finish very soon and so it's not
a good idea to create hundreds or even thousands of threads to begin with or
whenever there is a big load towards the threat,
put some of these tweets by the way are customizable,
so you can do things like let's go with 200 chunks.
Not really a problem because 200 threats will not be created and we'll
get better performance perhaps even 2000 chunks we can do And and you
can get some more improvements perhaps.
And again, the thread pool is smart,
it will not create too many threads at the same
time in an uncontrollable fashion,
compare that to try to create your own 2000 threats that's going to be
expensive and consume lots of memory and of course will will get lots of
diminishing returns and performance will suffer.
So working correctly with the thread pool is a challenge as well but at
least we didn't have to create any kind of threat here.
We just said to the thread pool, here's a function for you to run.
Just run it as soon as you can.
I don't care which threat does it.
As soon as as long as it's not happening on my thread