hey there how's it going everybody in
this video we're going to be learning
how to run code concurrently using the
threading module now this has been a
highly requested video for some time now
so I'm glad that I finally am getting
around to this now I'm also going to be
doing a follow-up video showing how to
run code in parallel using the multi
processing module and we'll also look at
when you would want to use one over the
other now if you don't know the
difference between threading and multi
processing then you should have a grasp
on the difference between those once
we're finished up now I would like to
mention that we do have a sponsor for
this video and that is brilliant dot org
so I really want to thank brilliant for
sponsoring this video and it would be
great if you all could check them out
using the link in the description
section below and support the sponsors
and I'll talk more about their services
in just a bit
so with that said let's go ahead and get
started ok so first why would we want to
use threading so basically we want to
use threading whenever it's going to
significantly speed up our program now
this speed-up comes from running
different tasks concurrently and we'll
talk more about concurrency and what
that means in just a second now speed
ups aren't really guaranteed so it
really just depends on what you're doing
so in this video we're gonna start off
with a basic example where we learn how
to run some simple sleep methods
concurrently but then we'll finish up
with a real-world example where we
download some high resolution images
from the internet now I want to show
that real world example because you know
personally when I watch tutorials that
only show how it works with a basic
example then I always feel like it
doesn't really not that I don't walk
away with any actual useful knowledge so
we'll use the sleep method to get a good
idea of how to use threading and then
we'll be sure to go over the more
complicated real world example of
downloading images online so let's go
ahead and get started so I have a
starting script open up here and if
you'd like to follow along then I'll be
sure to have a link to this code in the
description section below as well and
like I said we'll start with a very
simple example and see how this works
and then build up to more realistic
examples so for the script here I'm just
importing the time method and then I am
starting a counter this is just so we
can measure how long this entire script
takes so you really don't have to pay
attention at least on these lines here
but then we just have a simple function
called do something and this is printing
that we're going to sleep for one second
then we actually sleep for a second and
then I print that we are done
and then we are running that function
and then I am getting the finished time
here and then I'm printing out that our
script finished and I'm printing out the
total number of seconds okay so if I run
the code that we have right now we can
see that it said that we're sleeping for
one second that we're done sleeping and
that we finished in one second and that
sounds about right since we're running
the do something function one time and
that's sleeping for one second and if we
were to run that function twice then our
program will likely take two seconds so
let's go ahead and see that so I'm just
going to copy that and execute that
function again and if I run this then we
can see sleeping for a second sleeping
for a second finished in a little over
two seconds so we can see that each time
we run the do something function it's
adding about one second to our script
but it's not really doing anything on
the CPU in that function it's just
sleeping so our script is just waiting
around sleeping for a second and once
that's done it moves on to run that next
function and set around sits around
waiting for another second sleeping and
then at that point we're basically done
and our script finishes now I created a
quick graphic here to represent what
this looks like so let me pull this open
here in my browser so basically this is
what our script looks like right now
we're running a function here and then
it is sleeping for one second but this
is just us sitting around waiting and
after that second we run another
function here and then it comes up here
and waits
another second and then finally we are
done and running everything in order
like this is called running it
synchronously and so anytime our program
is running synchronously it actually
isn't doing much on the CPU and it's
just waiting around like this and that's
usually a good sign of when we can get
some benefits from using threading and
concurrency
now these are called CPU bound and i/o
bound task now CPU bound tasks are
things that are crunching a lot of
numbers and using the CPU and the i/o
bound tasks are things that are just
waiting for input and output operations
to be completed and not really using the
CPU all that much so some other examples
of i/o bound tasks include you know
reading and writing from the file system
and other file system operations network
operations downloading stuff online and
things like that
when it comes to threading we're gonna
see benefits when our tasks are IO bound
which again means that we're doing a lot
of waiting around for input and output
operations like you know reading data
from disk or network operations now if
our tasks are doing a lot of data
crunching and our CPU bound then we're
not going to get that much of a benefit
from using threading as a matter of fact
so some programs actually run slower
using threads because of the added
overhead cost when they're creating and
destroying different threads now if a
task is CPU bound then we'll likely want
to use multi processing and run it in
parallel instead and again we're gonna
take a look at that in the next video so
when we run something concurrently using
threads it's not actually going to run
the code at the same time it just gives
the illusion of running code at the same
time because when it comes to a point
where it's just waiting around it's just
going to go ahead and move forward with
the script and run other code while the
i/o operations finish and I've got
another graphic put together of what
this looks like so here's an example of
what our code would look like when we
use threading so we're gonna run this
function and we can see that it comes up
here and waits a second but as soon as
that starts waiting our code is just
gonna move on and go ahead and run
another function while we're waiting for
this one second to be done sleeping and
then we kick off this other second so
these are actually overlapped here but
we never actually ran any of this code
at the same time it's just going to give
the illusion of running the code at the
same time and then finally we can see
that we are done a little bit sooner now
this graph isn't a scale you know these
weights here would actually be much
shorter okay so now that we've actually
talked about concurrency and when to use
threads now let's see how to actually do
this with our current script
so first let's import the threading
module now this is in the standard
library so we don't need to install
anything so I'm going to come up here to
the top and I'm just going to import
threading now I'm going to show an older
way of how to do threading first so that
we can get a good idea of what's going
on but if it seems confusing at first
then definitely stick around because I'm
also going to show some newer ways of
doing threading using pools that allow
us to add threading to our programs with
a lot fewer lines
of code okay so first instead of running
the do something function twice in a row
like this let's instead turn both of
these into threads so to do this I'm
going to create two threads for both of
these so to do this I'm just going to
overwrite where we are setting these
functions here and I'm gonna create one
thread called t1 and this is going to be
threading dot thread and now we want to
set a target and our target is the
function that we want to run and that is
the do something function now we
actually want to pass in the function
here we don't want to execute the
function so don't put on parentheses
like this we just want to pass in the
function by itself uh neck's acute 'add
okay so now let me copy this line and
let's create another thread so I will
call this t2 and we will keep everything
else the same we're just creating a
thread with that target of do something
okay so at this point we've created two
thread objects but we're not actually
running that code so if I run this then
we can see that it finished immediately
but nothing from our function got
printed out so our function actually
didn't run so in order to get our
threads to run we need to use the start
method on each thread so here below T -
I'm just gonna say t1 dot start and t2
dot start and that will actually run our
thread but it not might not actually do
exactly what we think it'll do so if we
run this right now then we can see that
it now runs the functions and we can see
that it printed out the first lines from
both of those functions here let me make
this a little larger so it printed out
that both of our threads were going to
sleep for a second and then it said that
our script was finished in zero seconds
and then it printed out that it's done
sleeping now our script actually took
around one second to complete but the
reason that it says that it completed in
zero seconds is because it started both
of those threads and while the threads
were sleeping our script ran
concurrently and continued on with the
rest of the script so it immediately
came down and calculated our finish time
here and printed out our last print
statement as our threads were still
sleeping and then once that one second
was up our threads continued on and both
printed that they were done sleeping now
what if we
our threads to finish before we
calculated the finish time and before we
printed out that our script was finished
so in order to do this we can use the
join method so to do this just below our
starts I'm also going to run the join
method on both of these and that will
make sure that they complete before
moving on to calculate the finish time
and to print out this statement here
so if I save this and run it then we can
see that both of our threads started at
almost the exact same time and then they
both printed out that they were done
sleeping after a second and then our
script continued on to print that our
script finished after one second now if
using threads seems a bit complicated
right now
then definitely stick around until the
end of the video because we're gonna see
an easier way of how to do what we're
doing here but I think it's important to
understand what this is doing so far
even if we use other methods where we
don't manually call these start and join
methods okay so right now we're not
really getting that big of a speed-up
here so our code ran in two seconds
before and now it's running in one
second but that's because our function
doesn't take too long and we're only
running it twice but what if we wanted
to run our function ten times well if we
were to run our code synchronously where
it runs one after the other then we can
take a guess that it would take about
ten seconds since one would have to
finish before another started but if we
ran this with threads then it should
still be around one second to complete
so let's go ahead and see an example
with this now instead of manually
creating ten different threads let's
instead create and start these threads
and a loop so to do this I'm going to
come up here and copy this threading
part here and now I'm just going to
remove everything that we had here
before and now I'm just going to create
a loop so I'll say for underscore in
range of ten and then I'm gonna say T
for our thread is equal to threading dot
thread with a target of do something and
now let's also start that thread within
our loop now if none of you have ever
seen an underscore in Python here
basically this just means it's a
throwaway variable because all we want
to do is a loop over these ten numbers
here but we're not actually doing
anything with it
I just have an underscore variable so
that just to say that we're not using
anything within the loop okay so now we
have our loop here and our threads are
starting but we can't actually do a tea
or a thread dot join within the loop
because it would join on the thread
before looping through and creating and
starting the next thread so it would
basically be the same as running the
code synchronously so we need a way that
we can start all of these threads in one
loop and then loop through the threads
again and run the join method on them so
that they all finish before the end of
our script so to do this we can append
each thread that we create to a list of
threads so above the for loop here I'm
going to create a list of threads and
this is just going to be empty for now
and now below T dot start I'm going to
say threads dot append and we will
append each thread that we started so
now once we go through this loop we
should have a list of all these threads
that we started so now let's run join on
all of those so I'll say for thread in
threads and then we will do a thread dot
join ok so now we're actually running
this do something function ten times and
it sleeps for one second every time but
since we're using threads it's just
going to keep moving forward each time
we sleep so instead of taking ten
seconds let's run this and see how long
it takes so we can see all those got
kicked off and our scripts still
finished in just one second okay so
that's pretty good so we've already
taken a script that normally would take
ten seconds and we've instead finished
it in one second okay so now let's look
at how we can pass in arguments into our
function so right now we're running a
function that doesn't accept any
arguments but let's add a couple real
quick so we can see what this looks like
so right now we're just sleeping for one
second but let's add an argument that
specifies how long we actually want to
sleep so up here and our dosomething
function I'm going to pass in an
argument and I'll just call this seconds
and now I will say that we are sleeping
for however many seconds and this needs
to now be
and F string so that we can pass in the
variable like that and now instead of
sleeping for one second we'll sleep for
the number of seconds that we passed in
as a variable here and just so this
doesn't say sleeping for 10 second or
something like that let's also say that
this could be seconds as well okay okay
so now our function is expecting an
argument for the number of seconds to
sleep so let's pass in seconds as an
argument and we need to pass that in as
a list of arguments so down here where
we create our thread this is where we're
going to pass in our arguments so this
is just an args argument and we will
pass this in as a list now we only have
one argument so this is just going to be
a list of one value so let's sleep for
1.5 seconds and stell and instead so now
we should expect our function to take
about 1.5 seconds every time so if I
save this and run it then we can see
that now we are finished with our entire
script in 1.5 seconds and this still ran
at this function ten times so normally
this would take 15 seconds if we were
growing this synchronously but now we're
finishing it in 1.5 seconds okay so I
said before that I was going to show you
the older way of doing threads and then
I'd also show you what I believe is a
faster and easier way of doing this now
I still wanted to show you the manual
way of creating these threads because I
think this can still be useful depending
on what you're doing but I also think
it's better to learn this manual way
first to understand a little bit better
of what's going on in the background but
in Python 3.2 they added something
called a thread pool executor and in a
lot of cases this is going to be an
easier and more efficient way to run
these threads and it also allows us to
easily switch over to using multiple
processes instead of threads as well
depending on the problem that we're
trying to solve so let's replace what we
currently have and instead use this
thread pool executors now this is
actually not in the threading module
it's in the concurrent futures module so
at the top instead of using importing
the threading module I'm going to import
and I don't think I even need threading
anymore so I'm just going to import
current dot futures and when we use this
thread pool executors it's usually best
to use this with a context manager so
above our threads list here I'm just
going to do the same thing that we
already have
using the concurrent futures module
instead so to do this I'm just gonna say
with and let me go ahead and exit out so
we can see a little bit more of the
screen here so I'm gonna say with
concurrent dot futures dot and this is
thread pull whoops let me spell that
correctly thread pull executors and we
will say as executor there and now with
our executors there are a couple of
different methods that we can use so if
we want to execute the function once at
a time then we can use the submit method
so the submit method schedules a
function to be executed and returns a
future object so let's add this in and
I'll explain this a little bit more so
I'll say F 1 is equal to executors dot
submit and we will now pass in the
function and the arguments so I'm gonna
say do something is the function that we
want to run and we'll just pass in an
argument of one for one second so again
the submit method schedules a function
to be executed and returns a future
object now a future object basically
encapsulate s' the execution of our
function and allows us to check in on it
after it's been scheduled so we can
check that it's running or if it's done
and also check the result so if we grab
the result then it'll give us the return
value of the function now right now
we're just printing out values and not
returning anything but let me add a
return value so that we can grab that so
instead of printing out that we are done
sleeping here instead let me return that
string of done sleeping and okay let me
take out these parentheses here okay so
now we are returning that string instead
of printing it out so if we still want
to print that then we need to print the
return value of that function so let's
grab that by using the result method so
here within
the context manager I'm just going to
say print and we will print f1 dot
result now if we run the result method
here then it will actually wait around
until the function completes
okay so let's comment out what we had
before
so all of this code here is how we did
threads previously and now let's run
this code with our new thread pool
executors here okay so we can see that
that works and that's a lot less code
than we had down here that's commented
out and if we wanted to run this
multiple times then we could run submit
multiple times as well so above our f1
result let me create another future
object here so this will be F two we'll
just keep everything else the same and
now let's also print out the f2 result
so if I save that and run it then we can
see both of those got kicked off at the
same time and that we finished our
script in one second and if we wanted to
run this ten times like we did before
then we likely wouldn't want to run
submit ten two different times so we
could use a loop like we did before so
instead of running one at a time I'm
instead going to use a loop and we could
use a regular loop like we did before
but I'll go ahead and use a list
comprehension to create these instead so
we could say let me just copy this
entire submit line here and instead I'm
going to say results is equal to and now
let's create a list comprehension so I'm
going to say that we want to run
executors dot submit with our do
something function and an argument of
one second four and then just an
underscore for two as a throwaway
variable in range of ten now if you've
never used a list comprehensions before
then I do have a separate video on that
if you'd like to see exactly how this
works but again if you're not familiar
with list comprehensions then you can
use a regular for loop like we did down
here at the bottom okay so now we've
created a list comprehension that's
running our submit function ten
different times with an argument of one
sec
now in order to get these results we can
actually use another function from the
concurrent futures module called as
completed now this will give us an
iterator that we can loop over that will
yield the results of our threads as they
are completed so I think this is a
really useful method so to use this we
can just say I'm going to get rid of our
lines here and I'm just going to say for
F in concurrent dot futures and then dot
as underscore completed and we want to
pass in our results list here to our as
completed method and within this list we
can just say print F dot result so if we
run this then we can see that it still R
in it ran that ten times and if we
scroll down to the bottom here then we
can see that it still ran this in one
second now to prove that these are
actually coming in as they're completed
let me actually pass in a different
range of seconds for our threads to
sleep and those should print out in the
order that they complete so I'll create
a list of seconds to sleep here so above
our results I'm just going to create
another list and I will call this sex is
equal to and I'll do five four three two
and one
and now instead of use passing in one
here I'm instead going to pass in a
second for a second in our list of
seconds and I'm also going to print out
the seconds in the return statement as
well that way we can see which ones are
finishing and in what order so I'm going
to make this an F string as well and
just here at the end I will just print
out the seconds argument that we are
passing in so again what our list
comprehension here is doing is we are
submitting this do something second with
this argument of SEC and we are doing
that for each value of seconds in this
seconds list so this should get
submitted five times with a 5 4 3 2 and
a 1 so if I save this and run it then we
can see that it says sleeping 5 seconds
4 3 but then
the order that he's finished was that it
was done sleeping for one second first
then two then three them for them five
and our total script took five seconds
to complete now we actually started the
five-second thread first but since we
use that as completed method it printed
out our results in the order that they
completed okay so with the submit method
its submitting each function once at a
time now in order to run submit on an
entire list then we need to do a loop or
a comprehension like we did here but if
you're familiar with the built-in map
method in Python then there's actually
something similar that we can do with
threads where we can use a map method to
run our function over a list of values
now if you're familiar with that
built-in Python map method then this is
actually very similar except it uses
threads instead so it runs the function
with every item of the interval that we
pass in so let's say that I want to map
our function to our list of seconds so
to do this we could say here let me let
me just overwrite all of this that we
have now and instead I will use a map
method and say results is equal to
executors dot map and we want to map the
do something function and we want to
pass in our iterator of seconds here so
again what map is going to do is it is
going to run the do something function
with every value in this list of seconds
here now when we use the submit method
it returned a future object and when we
use map it instead returns the results
now it's going to still run those
threads concurrently but instead of
running the results as they completed
like we saw before map is going to
return the results and the order that
they were started so to loop over these
results we can just say for result in
results and then let's just print out
that result so if I run this then we can
see that it started off all of our
sleeps but nothing has returned yet and
then it returned the done sleeping for
five seconds four three two one
so we can see that all of
threads kicked off at pretty much the
same time but then it looked like they
completed all at the same time as well
now they didn't actually all complete at
the same time but when you loop over
your results using map like we did here
then it returns the results and the
order that they were started so since we
slept for five seconds first then we
waited for that one to finish before
printing out the other results but it
still didn't slow us down at all we can
see that our entire script still took
about five seconds in total to finish
now another thing to point out here is
that if our function raises an exception
then it won't actually raise the
exception while running the thread the
exception will be raised when its value
is retrieved from this results iterator
so if you need to handle exceptions then
you can do that within the iterator if
you'd like now if you'd like to learn
more about handling exceptions then I do
have a more in-depth video if you'd like
to learn more about that and I'll be
sure to leave a link to that video in
the description section below for anyone
who is interested now even if we don't
grab all of our results within the
context manager here it's still going to
automatically join all of those and let
them finish after the context manager
ends so if we comment out where we're
printing the results and I run this then
we can see that it still waits until
these are done until it gets down to the
end of our script so it didn't do like
it did before when we didn't have joins
and say that we finished in zero seconds
even though it actually wasn't finished
yet so it's still waited for the threads
in that thread pool to complete before
printing out that our script was done
okay so now that we've looked at a basic
example using sleep now let's take a
look at a more real-world example of
where threading would be useful so I've
got another script open here where I'm
not using threading at the moment so let
me pull this up and let's take a look at
what this is doing and again I'm gonna
have a link to this code in the
description section below for anyone who
wants to follow along so this is a
script that goes out and downloads some
high resolution photos from unsplash now
if you don't know what unsplash is it's
a website that has some really nice free
photos available for anyone to use so
let me go over this script and
show how someone might do this normally
so normally you might have a bunch of
image URLs here that you want to
download and now let's say we actually
want to download those so somebody might
say okay so for each image URL and our
image URLs let's use the request library
to go out and get that content and now
I'm just doing some string parsing here
to parse out the image name which would
just be this section right here so I'm
grabbing the image name and then putting
a dot jpg on to the end of that and now
I'm opening a file here in byte mode and
then I am writing those image bytes that
we downloaded from the internet onto our
file system and then we're just printing
out here that our image was downloaded
okay so this script should go all
through all of these image URLs and
download all of these images into my
current directory so if I run this right
now then let's see how long this takes
now we can see that these are
downloading and I do have this pulled up
here in my file system as well so we can
see that it is going out and downloading
these but it's downloading them one at a
time so there should be 15 total here so
once this is finished
then we can get an idea of how long this
took and it should be done any second
now okay so we can see that this
finished in 23 seconds to download those
15 high-resolution photos online now
when we're downloading a lot of things
online this is actually a great
candidate to use threading because this
is one of those IO bound operations that
is actually spending a lot of time just
waiting around it's not it's going out
and waiting for a response from the site
and it's not moving on to the next URL
until that gets an entire response back
so if we use threads that then it can
actually go ahead and move on to the
next URL while that's waiting around for
a response and we can make other
requests at the same time okay so now
let's see how we can change this code
that someone might normally write and
instead change this to where it's using
threads instead and see if we can speed
this up so first let's think about what
we're doing here
so we're looping over our list of image
URLs and then using the request
library to download those one at a time
so if we remember from our previous
example this would be a pretty good
candidate for the threading pull map for
the threading pull map method where we
can pass in a function and a list and
have that function run with every value
in that list
and if that doesn't make sense right now
then well it should make more sense once
we actually change this code here but
first we're gonna have to actually
create a function that will download the
data from a single URL so to do that we
can just say I'm just going to change
this for loop here and I'm gonna turn
this into a function so I'm gonna say
download underscore image and we want to
pass in an argument of the image URL and
everything within this function can just
stay the same because we were looping
over that before and now we just turn
that into a function where we are just
downloading one image URL at a time so
it's basically the same thing as our for
loop for now ok so now that we have a
function that downloads one image at a
time now we can create a thread pool and
map our list of URLs using that function
that we just created
so first let's import the concurrent
futures module so that we can use that
so here at the top I'm going to actually
let me put this below time here I will
import concurrent dot futures and now
down here below our function let's
create a thread pool executor so just
like before that was concurrent dot
futures and we're using this within a
context manager we want this to be a
thread pool executors make sure I spell
that correctly and we will say as
executor therefore our context manager
okay so now if we use that map method so
I'm going to say executors dot map and
we want to run this function and let's
pass in our list of image URLs and again
just to go over this one more time the
map method here what it
do is it will run this download image
function with each value in this image
URLs list but since we're using a thread
pool executors it's going to actually
down those download those with a
different thread for each one so just
with those small changes this will
actually use threads and make those
requests asynchronously instead of
synchronously like we saw before so now
if I run this code to download all these
images again then we can see that these
are coming in a lot faster because it's
using threads instead of doing this
synchronously so now we can see that it
finished in five seconds instead and if
you remember before we ran that it was
taking 23 seconds so that's a pretty
significant speed-up and this would be
even more significant if we were doing
even more requests so the speed ups can
be you know pretty drastic depending on
what we're actually doing now what would
be an example of something that wouldn't
be i/o bound well if something is doing
a lot of computation then threads
actually wouldn't be ideal for that type
of task so if we are processing the
photos and resizing them and things like
that instead of just downloading them
from line then the threads wouldn't
actually speed that up all that much so
that would actually be an example of
something that is CPU bound and not IO
bound and like I said before in those
kinds of scenarios threads can actually
slow down our script instead because the
threads have some overhead when being
created and destroyed so it really
depends on what you're doing and what
are to decide if the right choice to
speed up your program is threading or
multi-processing
so when you're doing something that
requires a lot of processing then we can
use multi processing instead of
threading so with that said in the next
video let's go ahead and process these
photos that we just downloaded and do
some image manipulation to them and then
we can take a look at the multi
processing module to see how we can
speed up that task as well that way we
can see the difference between threading
and multi processing and when you might
want to use one over the other now
before we finish up here I'd like to
mention the sponsor of this video and
that is brilliant org so we've been
talking a lot about thread
and multiprocessing and these topics are
especially useful in the field of data
science and data science is a field that
is growing at a very rapid pace if you'd
like to learn more about programming and
data science then I would definitely
recommend checking out brilliant org so
brilliant is a problem-solving website
that helps you understand underlying
concepts by actively working through
guided lessons and they've recently
added some brand-new interactive content
that makes solving puzzles and
challenges even more fun and hands-on
and if you'd like to learn more about
data science and programming with Python
then I would recommend checking out
their new probability course that covers
everything from the basics to real-world
applications and also fun things like
casino games they even use Python in
their statistics courses and will quiz
you on how to correctly analyze the data
within the language so they're guided
lessons will challenge you but you also
have the ability to get hints or even
solutions if you need them it's really
tailored towards understanding the
material they even have a coding barment
built into their website so that you can
run code directly in the browser and
that is a great complement to watching
my tutorials because you can apply what
you've learned in their active
problem-solving environment and that
helps to solidify that knowledge so to
support my channel and learn more about
brilliant you can go to brilliant org
forge / CMS to sign up for free and also
the first 200 people that go to that
link will get 20% off the annual premium
subscription and you can find that link
in the description section below and
again that is brilliant dot org forge /
CMS okay so I think that is going to do
it for this video I hope you feel like
you've got a good idea of how to use the
threading module and how we can use this
to speed up our scripts now there are
some more advanced topics that we could
cover in the future with threads such as
thread local data race conditions locks
and things like that but we'll save that
for a future video if anyone is
interested and like I said in the next
video we'll see how to use the multi
processing module to do the same thing
that we did in this video but instead
the task will be image processing
instead of downloading images but if
anyone has any questions about what we
covered in this video then feel free to
ask in the comment section below and
I'll do my best to answer those and if
you enjoy these tutorials and would like
to spore
them then there are several ways you can
do that the easiest ways to simply like
the video and give it a thumbs up and
also it's a huge help to share these
videos with anyone who you think would
find them useful and if you have the
means you can contribute to patreon and
there's a link to that page in the
description section below be sure to
subscribe for future videos and thank
you all for watching
you