Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding concurrency concepts to the syllabus #2645

Open
junedev opened this issue Mar 18, 2023 · 3 comments
Open

Adding concurrency concepts to the syllabus #2645

junedev opened this issue Mar 18, 2023 · 3 comments
Labels
x:rep/medium Medium amount of reputation

Comments

@junedev
Copy link
Member

junedev commented Mar 18, 2023

This is a placeholder issue for the work on concurrency related concepts and learning exercises.

Overview

This will be the first bit for teaching concurrency in the syllabus. Overall, we want to add the following concepts:

  • goroutines and waitgroups (two concepts in one exercise to allow for easier testing)
  • channels (potentially incl. range over channel)
  • select
  • mutex

Later we could add more things:

  • atomics
  • context
  • errgroup, syncmap and other helper stuff

Besides that, we also already have some unfinished work regarding net/http in store.

Important Docs

If you have not yet contributed to concept exercises before, the followig upfront reading is required to acquire the necessary background knowledge.

Here you can read about what Concept Exercises are and how they are structured:

Also, be aware of these general guidelines:

@junedev junedev changed the title New Concept Exercise: Goroutines and Waitgroups Adding concurrency concepts to the syllabus Mar 18, 2023
@sudomateo
Copy link
Contributor

sudomateo commented Aug 19, 2023

Hey @junedev! I'd like to start work on this.

Specifically this item:

  • goroutines and waitgroups (two concepts in one exercise to allow for easier testing)

Learning Objectives

Here's the learning objectives that I think would be beneficial for students to know.

Goroutines

  • Understand what a goroutine is and how it relates to an operating system thread.
  • Use goroutines to asynchronously execute tasks that should not block the main goroutine.
  • Describe the non-deterministic execution behavior of goroutines and how it can lead to data races.

Wait Groups

  • Understand what a WaitGroup is and how its API works.
  • Use a WaitGroup to synchronize the execution of multiple goroutines.
  • Describe what happens when WaitGroups are used incorrectly.

Exercise

This exercise is called "Task Runner". The student must program a function that concurrently executes a slice of functions provided by the caller, using a WaitGroup to ensure all of the goroutines have successfully finished executing.

The API for this function looks like this.

func TaskRunner(workFn ...func()) {}

The testing code will call this function, passing in various worker functions that sleep for a period of time. The implementation will have to execute these functions concurrently otherwise the test will timeout and fail. For example, if we pass in three worker functions that take 100, 200, and 300 milliseconds respectively, then the implementation should take no longer than 300 milliseconds to return +/- a few percent to account for scheduling and time variations. The test will use a context with a timeout to track this execution.

Here's example code for how the test will call the student's implementation.

func TestTaskRunner(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), 310*time.Millisecond)
	defer cancel()

	done := make(chan struct{})

	go func() {
		TaskRunner(
			func() { time.Sleep(100 * time.Millisecond) },
			func() { time.Sleep(200 * time.Millisecond) },
                        func() { time.Sleep(300 * time.Millisecond) },
		)

		done <- struct{}{}
	}()

	select {
	case <-done:
		// Success case. Do nothing on purpose.
	case <-ctx.Done():
		t.Fatal("Took too long! Failed!")
	}
}

Let me know what you think of these learning objectives and exercise and, once we finalize them, I can get to work on this.

@junedev
Copy link
Member Author

junedev commented Aug 29, 2023

Thanks for the proposal, looks good so far, some additional thoughts that came to mind...

Re the learning objectives for Goroutines:

  • Maybe it would be good to add some notes in the concept around the topic of "you should always be in control/know when the goroutine that you just kicked off will be done".
  • A common pitfall in a kind of "hello world" scenario is that someone will kick of a goroutine from main and then wonder why there program just terminates without the goroutine work being finished so this relation between main and other goroutines might be something we want to mention.
  • I think it would be good to include examples for both cases, a goroutine for some predefined named function and an example for an inline anonymous function.
  • Might be good to mention something around what happens when there is a closure (i.e. can a goroutine function access outside variables and should it?).

Re waitgroups:

  • Good point regarding incorrect usage, e.g. we might directly want to encourage to define the waitgroup as a pointer to avoid running into "accidental copying".
  • We might want to put waitgroups in context a bit by saying that Go's stdlib includes multiple "concurrency helpers" in the sync package and this is one of them.

Re the concepts in general:

  • Keep in mind that we have this distinction between "introduction" and "about" where the introduction is supposed to contain the minimal content to understand the basics of the concept and solve the exercise and everything else goes into the about. I'm not good at this myself as my introductions are usually a bit long and probably some points I mentioned above are more "about" content but I wanted to bring it up again anyway so you can do better. Also we still have the UI problem that the "about" is a bit hard to discover when you are on the site. (It's easy via google though as we show that one if you are not logged in.)

Re the exercise:

  • Great idea regarding how to test!
  • Some test case also needs to take into account the case where the student function finishes too early (does not wait the functions to be finished).
  • As a reminder, for the actual exercise the task(s) should be wrapped in some theme/story. We are trying to avoid purely technical descriptions like "you get some functions and you need to run them". Also the exercise name would rather follow the story. E.g. the exercise would be called "spring cleaning" and the functions that are passed in are cleanKitchen and cleanBathroom. Probably not the best example but hopefully you got the point.
  • I wonder whether there is more than one task here, more then one scenario we want the student to practice ... I need to think about this a bit more ... maybe a case where you would run the function multiple times and call Add with a different number, maybe something where the student needs to use something like i := i to resolve an concurrency issue ... (No blocker for you, if something comes to mind, I'll leave a note and we can discuss whether we want to add something. Or maybe you have an idea.)

@junedev
Copy link
Member Author

junedev commented Aug 29, 2023

Sidenote: For the concurrency exercises it probably makes sense to activate the race detector for the automated test runs that Exercism does, you can read how to do that here https://github.com/exercism/go-test-runner#providing-additional-testing-flags .

@junedev junedev added the x:rep/medium Medium amount of reputation label Aug 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
x:rep/medium Medium amount of reputation
Projects
None yet
Development

No branches or pull requests

2 participants