1 00:00:06,600 --> 00:00:09,420 - We've already seen how to join a single thread. 2 00:00:09,420 --> 00:00:11,970 In other words, how to wait for that thread to finish. 3 00:00:11,970 --> 00:00:15,450 You simply spawn the thread and get back the handle. 4 00:00:15,450 --> 00:00:18,030 And then on the handle, you call the join function. 5 00:00:18,030 --> 00:00:21,090 The join function waits for that thread to terminate 6 00:00:21,090 --> 00:00:24,270 either successfully or unsuccessfully. 7 00:00:24,270 --> 00:00:27,570 And then once the join method has returned, 8 00:00:27,570 --> 00:00:29,070 it gives you back a result. 9 00:00:29,070 --> 00:00:31,530 You can then either unwrap the result if you're confident 10 00:00:31,530 --> 00:00:35,040 that it's worked correctly, or you can do a pattern matching 11 00:00:35,040 --> 00:00:38,310 on the result to see if it was okay or if it was an error. 12 00:00:38,310 --> 00:00:41,520 So that's how you wait for a single thread to finish. 13 00:00:41,520 --> 00:00:43,980 What if you want to wait for multiple threads to finish? 14 00:00:43,980 --> 00:00:46,530 Let's say you've kicked off five threads 15 00:00:46,530 --> 00:00:48,360 and you want to wait for all of them to finish 16 00:00:48,360 --> 00:00:49,860 before you continue. 17 00:00:49,860 --> 00:00:52,320 So let's see how to do this. 18 00:00:52,320 --> 00:00:55,740 To join multiple threads, you'd simply create a collection 19 00:00:55,740 --> 00:00:58,080 to hold all the individual thread handles. 20 00:00:58,080 --> 00:00:59,850 So it might look like this. 21 00:00:59,850 --> 00:01:02,730 I've created a vector, it's mutuable 22 00:01:02,730 --> 00:01:04,050 because I'm gonna be changing the state 23 00:01:04,050 --> 00:01:06,180 of the items in here. 24 00:01:06,180 --> 00:01:07,953 The vector contains joinHandles, 25 00:01:09,572 --> 00:01:11,673 one joinHandle per thread. 26 00:01:12,510 --> 00:01:13,980 From a technical perspective, 27 00:01:13,980 --> 00:01:16,020 joinHandle is a generic structure. 28 00:01:16,020 --> 00:01:21,020 So when you specify the vector as holding joinHandles, 29 00:01:21,480 --> 00:01:24,420 you have to give it a type parameter here. 30 00:01:24,420 --> 00:01:26,580 In fact, you don't have to give it a type parameter. 31 00:01:26,580 --> 00:01:31,050 You can use an underscore to tell Rust I don't really care 32 00:01:31,050 --> 00:01:32,670 what the type parameter's going to be. 33 00:01:32,670 --> 00:01:34,290 Okay, so the compiler then just kind 34 00:01:34,290 --> 00:01:35,973 of ignores the type parameter. 35 00:01:36,960 --> 00:01:39,750 So what I've created here is an empty vector, 36 00:01:39,750 --> 00:01:41,910 which will eventually contain the handles 37 00:01:41,910 --> 00:01:43,803 of all the threads I've created. 38 00:01:44,790 --> 00:01:47,700 So every time you spawn a thread, 39 00:01:47,700 --> 00:01:51,030 add this handle into the collection like this. 40 00:01:51,030 --> 00:01:54,300 In this example here, I've looped round five times. 41 00:01:54,300 --> 00:01:57,150 Each time around the loop, I spawn another thread 42 00:01:57,150 --> 00:01:58,770 and I get back its handle 43 00:01:58,770 --> 00:02:01,470 and I just add that handle into my collection. 44 00:02:01,470 --> 00:02:04,020 So this collection will contain the handles 45 00:02:04,020 --> 00:02:05,583 for all five threads. 46 00:02:07,410 --> 00:02:09,300 So on the main thread, you then go off 47 00:02:09,300 --> 00:02:11,280 and do whatever work you want to do. 48 00:02:11,280 --> 00:02:12,960 And at the point where you're finished 49 00:02:12,960 --> 00:02:14,640 and you're then ready to just wait 50 00:02:14,640 --> 00:02:16,140 for those other threads to finish, 51 00:02:16,140 --> 00:02:19,920 what you do is just call join on all those thread handles, 52 00:02:19,920 --> 00:02:22,050 literally just like this. 53 00:02:22,050 --> 00:02:23,670 You write the loop to iterate 54 00:02:23,670 --> 00:02:27,240 through the collection of handles and you basically wait 55 00:02:27,240 --> 00:02:30,000 for each handle to terminate one by one. 56 00:02:30,000 --> 00:02:32,250 So if we had five threads, 57 00:02:32,250 --> 00:02:35,100 then this code would loop five times. 58 00:02:35,100 --> 00:02:38,580 The first thread handle, we'd wait for that to finish 59 00:02:38,580 --> 00:02:41,610 and then maybe unwrap its content or do a pattern match. 60 00:02:41,610 --> 00:02:42,930 And then we loop around waiting 61 00:02:42,930 --> 00:02:46,080 for the next thread to finish and so on. 62 00:02:46,080 --> 00:02:48,870 So eventually, by the time this loop terminates, 63 00:02:48,870 --> 00:02:51,810 all those five threads will have joined, 64 00:02:51,810 --> 00:02:53,973 will have finished and we're done. 65 00:02:54,930 --> 00:02:58,023 So the example is in lesson14_multithreading_concurrency. 66 00:02:59,790 --> 00:03:02,610 We'll look in main.rs first as usual. 67 00:03:02,610 --> 00:03:05,100 And then the code we're gonna look at in particular, 68 00:03:05,100 --> 00:03:06,203 demo_joining_thread_multiple. 69 00:03:07,380 --> 00:03:08,850 And then obviously, we'll run the program. 70 00:03:08,850 --> 00:03:10,290 I'm gonna run it several times 71 00:03:10,290 --> 00:03:12,210 with different scenarios again. 72 00:03:12,210 --> 00:03:14,640 So should we take a look at the code? 73 00:03:14,640 --> 00:03:17,970 Here's the project, and here's my main code 74 00:03:17,970 --> 00:03:22,160 in my main code at demo_joining_thread_multiple. 75 00:03:23,971 --> 00:03:25,650 There we go and it's here. 76 00:03:28,170 --> 00:03:30,330 Right, so let me run you through the code 77 00:03:30,330 --> 00:03:32,801 that we have in this example. 78 00:03:32,801 --> 00:03:34,350 Here is my collection, 79 00:03:34,350 --> 00:03:37,590 my vector that's going to hold all my join handles 80 00:03:37,590 --> 00:03:40,200 for all the threads I'm going to create. 81 00:03:40,200 --> 00:03:42,120 I loop around five times. 82 00:03:42,120 --> 00:03:44,583 Each time on the loop, I spawn another thread. 83 00:03:45,510 --> 00:03:47,249 So without looking at the details 84 00:03:47,249 --> 00:03:51,090 of the closure for each thread just yet. 85 00:03:51,090 --> 00:03:53,310 And because that's kind of secondary really, 86 00:03:53,310 --> 00:03:55,290 what it does inside doesn't really 87 00:03:55,290 --> 00:03:57,360 of our concern on the outside. 88 00:03:57,360 --> 00:03:58,830 We'll have a look at that in a minute. 89 00:03:58,830 --> 00:04:00,720 The main thing is that we spawn the thread. 90 00:04:00,720 --> 00:04:03,780 We'll get back a handle for that thread. 91 00:04:03,780 --> 00:04:04,613 We take that handle, 92 00:04:04,613 --> 00:04:07,440 we push it into our collection of all handles. 93 00:04:07,440 --> 00:04:09,030 So our collection of all handles 94 00:04:09,030 --> 00:04:11,923 will now contain five JoinHandles. 95 00:04:13,189 --> 00:04:14,670 In my main code, 96 00:04:14,670 --> 00:04:17,973 I'm going to loop around a few times being busy. 97 00:04:19,500 --> 00:04:22,503 So whilst these background threads are doing their thing, 98 00:04:23,400 --> 00:04:27,390 my main thread is also being busy in its foreground task. 99 00:04:27,390 --> 00:04:31,440 It's gonna loop around five, six times, display a message 100 00:04:31,440 --> 00:04:35,550 and sleep for half a second, 500 milliseconds. 101 00:04:35,550 --> 00:04:39,240 So that's gonna take three seconds to complete. 102 00:04:39,240 --> 00:04:41,070 That's not very long really. 103 00:04:41,070 --> 00:04:44,880 And then we'll wait for those other threads to terminate. 104 00:04:44,880 --> 00:04:45,960 So the main thread will wait 105 00:04:45,960 --> 00:04:47,160 for those other threads to finish. 106 00:04:47,160 --> 00:04:49,740 So we loop around all the handles. 107 00:04:49,740 --> 00:04:52,530 For each handle, the join function 108 00:04:52,530 --> 00:04:54,120 basically means wait, doesn't it? 109 00:04:54,120 --> 00:04:56,730 Wait for that other thread to terminate? 110 00:04:56,730 --> 00:04:59,760 I haven't got any panics in my other threads. 111 00:04:59,760 --> 00:05:01,950 So there's not going to be any error conditions 112 00:05:01,950 --> 00:05:03,780 that we need to worry ourselves about. 113 00:05:03,780 --> 00:05:07,083 So it's okay to unwrap the result and just move on. 114 00:05:08,220 --> 00:05:09,420 So should we take a closer look 115 00:05:09,420 --> 00:05:10,710 at what's actually gonna happen? 116 00:05:10,710 --> 00:05:14,430 Because five times I'm going to be spawning a new thread. 117 00:05:14,430 --> 00:05:18,570 So I'm gonna basically run this closure five times 118 00:05:18,570 --> 00:05:21,780 and I've got an element of randomness 119 00:05:21,780 --> 00:05:25,406 to make the example a little bit less predictable. 120 00:05:25,406 --> 00:05:29,850 Okay, so rand, I'm not sure if we've seen that before. 121 00:05:29,850 --> 00:05:34,680 The rand crate, which I've imported here, 122 00:05:34,680 --> 00:05:38,100 the rand crate not part of the standard crate. 123 00:05:38,100 --> 00:05:40,590 It's a separate crate, separate dependency. 124 00:05:40,590 --> 00:05:41,700 So I've imported 125 00:05:41,700 --> 00:05:46,700 or I've specified the rand dependency, which is good. 126 00:05:46,770 --> 00:05:50,310 And then in the rand crate, 127 00:05:50,310 --> 00:05:54,003 there's a thread random number generator. 128 00:05:55,470 --> 00:05:57,030 So that's a function 129 00:05:57,030 --> 00:06:00,660 and it'll give you back a random number generator. 130 00:06:00,660 --> 00:06:02,970 And then upon that random number generator, 131 00:06:02,970 --> 00:06:06,960 you can ask it to generate a number in a specified range. 132 00:06:06,960 --> 00:06:11,340 Generate the number in the range between five and 10. 133 00:06:11,340 --> 00:06:15,930 So that would be each time I spawn another thread. 134 00:06:15,930 --> 00:06:17,520 So I'm gonna do this five times. 135 00:06:17,520 --> 00:06:19,800 Each time I spawn another thread, 136 00:06:19,800 --> 00:06:23,040 I'm gonna get a different random number each time. 137 00:06:23,040 --> 00:06:25,530 So each thread will have a different random number 138 00:06:25,530 --> 00:06:28,830 and it uses that to sleep for a random amount of time. 139 00:06:28,830 --> 00:06:31,920 So each thread will sleep for a random amount of time, 140 00:06:31,920 --> 00:06:34,110 between five seconds and 10 seconds. 141 00:06:34,110 --> 00:06:36,420 And of course, that's happening concurrently. 142 00:06:36,420 --> 00:06:37,590 It's not consecutive. 143 00:06:37,590 --> 00:06:40,740 These threads are running or sleeping at the same time. 144 00:06:40,740 --> 00:06:44,880 So in total, it's probably gonna take maximum 10 seconds 145 00:06:44,880 --> 00:06:47,580 and a bit for this application to terminate. 146 00:06:47,580 --> 00:06:51,180 If the longest that a thread has to wait is 10 seconds, 147 00:06:51,180 --> 00:06:54,720 the other threads will also be spinning at the same time. 148 00:06:54,720 --> 00:06:59,520 So that random number between five and 10, 149 00:06:59,520 --> 00:07:02,070 that's the length of time I sleep for. 150 00:07:02,070 --> 00:07:06,240 This thread will sleep for that number of seconds. 151 00:07:06,240 --> 00:07:09,690 And it says when it goes to sleep with its id 152 00:07:09,690 --> 00:07:10,890 and it says when it wakes up. 153 00:07:10,890 --> 00:07:13,200 So we're gonna see pairs of messages appearing 154 00:07:13,200 --> 00:07:14,100 on the screen. 155 00:07:14,100 --> 00:07:16,590 Thread number two has started to sleep, 156 00:07:16,590 --> 00:07:17,490 thread number three, 157 00:07:17,490 --> 00:07:19,650 thread number four, thread number five. 158 00:07:19,650 --> 00:07:21,420 Oh, thread number two has finished its sleep. 159 00:07:21,420 --> 00:07:25,290 So threads kind of enter and then exit the sleep state. 160 00:07:25,290 --> 00:07:26,123 And that's it. 161 00:07:26,123 --> 00:07:29,310 Once the thread has finished sleep then and woken up, 162 00:07:29,310 --> 00:07:30,450 then it terminates. 163 00:07:30,450 --> 00:07:33,270 And at that point, its join handle gets signaled 164 00:07:33,270 --> 00:07:35,130 to say I've finished. 165 00:07:35,130 --> 00:07:38,460 And eventually, our main code down here will have waited 166 00:07:38,460 --> 00:07:40,160 for all five threads to terminate. 167 00:07:42,180 --> 00:07:44,080 So I feel like we should run this now. 168 00:07:51,660 --> 00:07:54,150 Okay, so you can see those background threads 169 00:07:54,150 --> 00:07:55,980 have started their wait. 170 00:07:55,980 --> 00:07:58,440 The main thread was busy for three seconds, 171 00:07:58,440 --> 00:08:01,381 displaying 101 to 105. 172 00:08:01,381 --> 00:08:03,960 Thread number one then entered a wait state, 173 00:08:03,960 --> 00:08:06,330 waiting for those other threads to finish. 174 00:08:06,330 --> 00:08:08,310 And those other threads, 175 00:08:08,310 --> 00:08:10,230 they kind of finished in a random order. 176 00:08:10,230 --> 00:08:12,257 Thread number two was quite late to finish. 177 00:08:12,257 --> 00:08:14,850 Thread number three was quite soon. 178 00:08:14,850 --> 00:08:16,260 Thread six was the soonest 179 00:08:16,260 --> 00:08:20,940 because each thread had a different random sleep amount. 180 00:08:20,940 --> 00:08:24,390 So you can see that the first join handle to terminate 181 00:08:24,390 --> 00:08:27,510 was thread number... 182 00:08:27,510 --> 00:08:29,910 Oh, this, what I should actually say 183 00:08:29,910 --> 00:08:31,893 is this is the print statement here. 184 00:08:34,920 --> 00:08:38,313 So that's the print statement to say thread six, 185 00:08:40,140 --> 00:08:42,660 which started at sleep here, 186 00:08:42,660 --> 00:08:45,123 and thread six was gonna sleep for five seconds. 187 00:08:46,170 --> 00:08:48,810 That's the shortest compared to all the other threads. 188 00:08:48,810 --> 00:08:51,240 All the other threads are longer than that. 189 00:08:51,240 --> 00:08:53,790 So ThreadId six started sleeping 190 00:08:53,790 --> 00:08:57,540 for six seconds, for five seconds I should say. 191 00:08:57,540 --> 00:09:01,467 So that would be the first thread to terminate here. 192 00:09:01,467 --> 00:09:04,050 And then the other threads all terminate. 193 00:09:04,050 --> 00:09:06,660 And my main code, which is waiting 194 00:09:06,660 --> 00:09:09,720 for all those handles to be released effectively, 195 00:09:09,720 --> 00:09:13,020 when all five threads are finished, it'll realize 196 00:09:13,020 --> 00:09:15,903 that it's time for the application to terminate.