We may earn an affiliate commission when you visit our partners.
Mark Farragher

Do you know how to write robust multi-threaded C# code that does not crash?

Lets face it: writing multi-threaded code is hard. The sobering truth is that, unless you know exactly what you're doing, your code is pretty much guaranteed to crash in production.

Don't let this happen to you.

It doesn't have to be like this. If you have a good understanding of multi-threaded programming and follow a few simple industry best practices, you can write robust code that can take a beating.

Read more

Do you know how to write robust multi-threaded C# code that does not crash?

Lets face it: writing multi-threaded code is hard. The sobering truth is that, unless you know exactly what you're doing, your code is pretty much guaranteed to crash in production.

Don't let this happen to you.

It doesn't have to be like this. If you have a good understanding of multi-threaded programming and follow a few simple industry best practices, you can write robust code that can take a beating.

I wrote a multi-threaded conversion utility a few years ago, that successfully migrated 100,000 documents from SharePoint 2010 to SharePoint 2013. The program worked flawlessly the first time, because I implemented all of the best practices for writing asynchronous C# code.

Sound good?

In this course I am going to share these practices with you.

In a series of short lectures I will cover many multi-threading topics. I will show you all of the problems you can expect in asynchronous code, like race conditions, deadlocks, livelocks and synchronisation issues. I'll show you quick and easy strategies to resolve these problems.

By the end of this course you will be able to write robust multi-threaded C# code that can take a beating.

Why should you take this course?

You should take this course if you are a beginner or intermediate C# developer and want to take your skills to the next level. Asynchronous programming might sound complicated, but all of my lectures are very easy to follow, and I explain all topics with clear code and many instructive diagrams. You'll have no trouble following along.

Or maybe you're working on a critical section of code in a multi-threaded C# project, and need to make sure your code is rock-solid in production? The tips and tricks in this course will help you immensely.

Or maybe you're preparing for a C# related job interview? This course will give you an excellent foundation to answer any threading-related questions they might throw at you.

Enroll now

Here's a deal for you

We found an offer that may be relevant to this course.
Save money when you learn. All coupon codes, vouchers, and discounts are applied automatically unless otherwise noted.

What's inside

Learning objectives

  • Learn to write multi-threaded code
  • Safely abort a thread
  • How can two or more threads exchange data?
  • The main reasons why multi-threaded code crashes
  • Learn to resolve race conditions
  • Use autoresetevent and manualresetevent in your code
  • The dining philosopher problem
  • Line up threads with the barrier class
  • ... and much more!

Syllabus

Introduction

In this lecture I explain how this course is organised and I describe each of the upcoming sections in detail.

Read more

In this lecture we're going to look at the theory behind asynchronous programming. What exactly is multithreaded code, and how does it work?

Many lectures in this course contain source code examples. Feel free to download the code and follow along. And here's the good news: it doesn't matter if you have a Window, Mac or Linux computer. The code will run on all three operating systems.

In this lecture I demonstrate how my solutions and projects run on all operating systems. I will show you how to build and run the source code on a Mac, on Linux and in Visual Studio running on Windows 8.

At the end of this lecture you will have learned that .NET code is portable and can run on at least five different operating systems.

Welcome to the Thread Class section. I will give a quick introduction on how the section is organized before we get started.

In this lecture, I will teach you how to start new threads using the System. Thread class: the workhorse of multi-threaded programming in C#.

I will also show you how you can give a descriptive name to a thread, to aid in debugging.

Finally, you will learn that there are two classes of threads: foreground- and background threads. I will show you the difference in behavior between these two classes of threads.

In this lecture I am going to show you the most common multi-threading programming problem: a race condition.

A race condition happens when 2 or more threads are trying to access and modify the same variable. I will demonstrate a race condition with a very simple program, with 2 threads accessing a shared integer class member.

In the next section I will show you a comprehensive solution for dealing with race conditions. For now I will leave you with a tip on how to minimize the impact of race conditions in your code.

In this lecture you will learn how to safely pass in initialization data to a thread. You will learn about the ParameterizedThreadStart delegate, and how to use a lambda expression to initialize a thread.

Captured variables in lambda expressions are shared between the new thread and the main program thread, and so this opens us up to a possible race condition.

I will show you a short program that uses a lambda expression and introduces a race condition. Then I'll show you a cool trick, where I only change 2 lines of code, to make the race condition disappear.

In this lecture I am going to show you another common multi-threading programming problem: checking if a thread has finished.

I will show you a short multi-threaded program with a block of code that I want to be executed once. Two threads check each other's state to ensure that the code executes only a single time. I'll show you a working solution that actually has a big hidden problem. You will learn that the program only works by pure coincidence.

I will conclude the lecture with some advice on the best way to check if a thread has finished.

In this lecture you will learn how to suspend the current thread until another thread has completed, using the Join method. We will revisit the multi-threaded program with the race condition from the previous lecture. I will show you how a single strategically placed Join statement resolves the race condition.

In the second part of this lecture I will show you how you can suspend the current thread for a given time interval by using the Sleep method.

We'll conclude with a summary of what we have learned.

The previous lecture introduced the Join and Sleep methods which suspend the current thread until either another thread ends, or when a given timeout expires.

In this lecture you will learn how to interrupt and abort suspended threads. We will look in detail at what precisely happens when you interrupt or abort a suspended or a non-suspended thread.

Even though the Interrupt and Abort methods look really useful, using them in practice is somewhat risky. We'll look at how an unexpected interrupt or abort can introduce resource leaks, and I will provide two scenarios in which you can safely abort a thread without having to worry about leaks.

Congratulations on finishing this section. This is a recap of what we have learned.

Test your knowledge of the thread class with this short quiz.

Welcome to the Thread Locking section. I will give a quick introduction on how the section is organized before we get started.

In the previous section I showed you several multi-threaded programs that were prone to a specific problem called a 'race condition'.

In this lecture I revisit the race condition and I'll demonstrate how a special technique called 'thread locking' resolves the problem. I will show you several short examples of code prone to race conditions, and then I'll add thread locking to the code to fix the problem.

At the end of this lecture you will know exactly what thread locking is, how it resolves a race condition, and when you should implement it yourself.

In this lecture we are going to take a closer look at the lock statement in C#. You will learn that "lock" is in fact syntactic sugar for a pair of Monitor.Enter and Monitor.Exit calls. I will demonstrate several example programs using either the compact "lock" syntax, or the more verbose code that uses the Monitor class.

You will learn all the essentials of thread locking, including what code to lock, which synchronisation object to use, and what the advantages are of calling the Monitor class directly.

By the end of the lecture you will be proficient in thread locking, and you will be able to set up critical sections in your code with ease.

This lecture explains how to deal with deadlocks. A deadlock is a problem that occurs when two or more threads are waiting indefinitely for each other, trying to access and lock two or more resources.

I will explain deadlocks in detail using the famous thought experiment created by Edsger Dijkstra in 1965: the "Dining Philosopher" problem. I will show you what a deadlock looks like when represented in the context of the dining philosophers.

We will then examine two quick-fix strategies for resolving deadlocks: introducing randomness, or use an arbiter.

After completing this lecture you will have a thorough understanding of what a deadlock is, and you will know two strategies for resolving deadlocks in your code. You will also be aware of the Chandy / Misra algorithm, which is the reference solution for the Dining Philosopher problem.

In this lecture we revisit the Dining Philosopher problem. I have written a simulation program that sets up all 5 philosophers and chopsticks, and implements the random sleep mitigation strategy that we discussed earlier. You will see that my implementation has terrible performance, with all 5 philosophers fighting for the chopsticks more than 90% of the time.

Can you do better than me?

Your assignment is to take my code as a starting point and write your own improved deadlock resolving strategy. The objective is to have all philosophers eat for as long as possible. The highest score you can achieve is slightly over 20 seconds.

Good luck!

In this lecture we are going to take a closer look at a specific scenario: locking and incrementing a single variable. You already learned that you can make an increment operation thread-safe by using a lock statement. But unfortunately a lock has a performance overhead which will slow down your code.

Fortunately there is an alternative. For simple scenarios like incrementing, decrementing, reading or writing a single variable, you can also use the Interlocked class. The Interlocked class exposes low-level thread-safe CPU operations which perform much better than a generic lock statement.

In this lecture we are going to take a closer look at the performance difference between thread-unsafe and thread-safe code, and between the generic lock statement and the Interlocked class.

By the end of this lecture you will have learned if using the Interlocked class is worth the effort.

Test your knowledge of thread locking with this short quiz.

Welcome to the Thread Synchronisation section. I will give a quick introduction about how the section is organised before we get started.

In this lecture I am going to take a look at thread synchronisation. The need for thread synchronisation arises when two or more threads need to exchange data in a controlled manner. I will show you a simple example program that attempts to exchange data between threads without any synchronisation, and you will see how the data transfer completely fails.

Next we will cover the workhorse of thread synchronisation: the AutoResetEvent. I will show you how you can line up two threads with a single AutoResetEvent variable, to ensure that you'll never lose any data. Then I'll show you how the single remaining race condition can be resolved by adding a second AutoResetEvent.

By the end of the lecture you will have a deep understanding of thread synchronisation: what it is, when you need it, and how you can implement it yourself.

In this lecture I am going to build a producer/consumer queue which is a very popular multi-threaded coding pattern. The queue features one or more 'producers' which add tasks to a shared queue, and a pool of 'consumers' that retrieve tasks from the queue and execute them in the background.

I will show you how you can build a producer/consumer queue in .NET with only a simple thread-safe queue of delegates, and one AutoResetEvent to notify consumers that a new task is available.

By the end of the lecture you will be able to build your own producer/consumer queue, and you will also have learned a surprising fact about the console.

So far we've only used AutoResetEvents to synchronise two or more threads. In this lecture I'm going to take a closer look at the ManualResetEvent, a wait handle similar to the AutoResetEvent but with a slightly different behaviour.

I will start with the producer/consumer queue from the previous lecture, and add new pause and resume functionality. I will show you what happens when you try and build that functionality with an AutoResetEvent (hint: it doesn't work). Then I'll show you how the ManualResetEvent behaves, and I will change the code to make the queue work as intended.

By the end of the lecture you will have a clear understanding of the differences between an AutoResetEvent and a ManualResetEvent, and you will have learned how to use the latter in the producer/consumer queue to make all consumers pause or resume their work.

You have seen how the AutoResetEvent can be used to signal an event from one thread to another, and how the ManualResetEvent can be used to signal from one thread to an entire group of threads.

In this lecture I am going to cover a third scenario: how to signal from a group of threads to a single thread. I will show you how the CountdownEvent, a new type of wait handle, can be used to implement this scenario. I will revisit the producer / consumer queue, and use a CountdownEvent to add a new feature that lets me quit all consumers simultaneously.

By the end of the lecture you will have a clear understanding of the differences between the AutoResetEvent, the ManualResetEvent and the CountdownEvent.

In this lecture I will describe 'Thread Rendezvous' which is the process of aligning two or more threads in time, to execute the same part of code simultaneously.

There are several ways to implement thread rendezvous, and you have already seen one method that uses two complimentary AutoResetEvents to synchronise two threads. I will show you two other techniques that solve several problems with the AutoResetEvents solution.

By the end of the lecture you will have learned what the Barrier class is for, and how it solves several problems that pop up when you try to implement thread rendezvous with AutoResetEvents or a CountdownEvent.

In this lecture I would like to thank you for finishing the course and offer some final words.

Traffic lights

Read about what's good
what should give you pause
and possible dealbreakers
Explores race conditions, deadlocks, and synchronization issues, which are common challenges in multi-threaded C# development, offering practical strategies to resolve them effectively
Covers industry best practices for writing asynchronous C# code, which can help developers write robust code that can handle demanding workloads and prevent crashes in production environments
Includes a section on thread locking, which is essential for managing shared resources and preventing race conditions in multi-threaded applications, ensuring data integrity and program stability
Features a lecture on the Dining Philosopher problem, which is a classic example used to illustrate deadlocks, providing learners with insights into deadlock prevention and resolution techniques
Uses the .NET framework, which is cross-platform and allows developers to write code that can run on Windows, Mac, and Linux operating systems, increasing the versatility of the skills learned
Requires learners to have access to Visual Studio, which is a professional IDE that may require a paid license depending on the edition and usage scenario, potentially posing a barrier to entry

Save this course

Create your own learning path. Save this course to your list so you can find it easily later.
Save

Reviews summary

Robust multi-threaded c# fundamentals

According to learners, this course provides a solid foundation in the fundamentals of multi-threaded C# programming. Students found the explanations of complex topics like race conditions, deadlocks, and synchronization issues to be particularly clear and easy to follow. The code examples and practical demonstrations are frequently highlighted as major strengths, helping to solidify understanding and show how to implement solutions. While many felt the course was an excellent introduction and highly practical for writing robust code, some reviews, especially more recent ones, suggested it would benefit from covering more modern C# features like async/await or delving deeper into advanced performance topics.
Covers traditional threading extensively.
"The course focuses heavily on the older `Thread` class and basic synchronization primitives."
"It covers classic multi-threading problems like the Dining Philosopher problem in detail."
"Excellent coverage of `lock`, `Monitor`, and various `ResetEvent` types."
"It's more about traditional threading than modern async/await patterns."
Well-suited for stated target audience.
"As an intermediate C# developer, I found this course pitched perfectly for me."
"Great for someone just starting out with multi-threading in C#."
"The course description accurately describes who would benefit most from this content."
"If you need a gentle but thorough introduction, this is it."
Excellent introduction to core multi-threading.
"This course gave me a really solid foundation in multi-threaded programming using C#."
"It's a great starting point if you're new to writing concurrent applications."
"Learned the essential techniques like thread locking and using wait handles."
"Feel much more confident tackling multi-threaded code after this course."
Practical code demos reinforce learning.
"The code examples were extremely helpful and demonstrated the concepts perfectly."
"Being able to download and run the code alongside the lectures made a huge difference."
"I found the practical programs shown in the course really helped solidify my understanding of how to implement these techniques."
"Seeing the race conditions and deadlocks occur in the demo code was very illustrative."
Breaks down complex multi-threading concepts.
"The instructor did a great job explaining complex concepts like race conditions and deadlocks in a clear and concise manner."
"Understanding multi-threading can be tricky, but this course explained the core problems and solutions very clearly."
"I appreciated the simple language and diagrams used to illustrate difficult topics."
"The way synchronisation issues were explained just clicked for me."
Limited coverage of async/await.
"I wish the course covered more modern C# async/await patterns, which are more common now."
"While the fundamentals are good, the lack of async/await makes it feel slightly dated."
"Could use an update to include Task Parallel Library and async/await concepts."
"Learned a lot about older methods, but felt I missed out on the newer, preferred approaches."

Activities

Be better prepared before your course. Deepen your understanding during and after it. Supplement your coursework and achieve mastery of the topics covered in How To Write Bulletproof Multi-Threaded C# Code with these activities:
Review C# Fundamentals
Reviewing C# fundamentals will ensure a solid base for understanding the more complex multi-threading concepts covered in the course.
Show steps
  • Review data types, control structures, and object-oriented programming concepts in C#.
  • Practice writing simple C# programs to reinforce your understanding.
Review 'C# 8.0 and .NET Core 3.0 – Modern Cross-Platform Development'
Reading this book will provide a broader understanding of C# and .NET Core, which will help you better grasp the multi-threading concepts.
Show steps
  • Read the chapters related to asynchronous programming and threading.
  • Experiment with the code examples provided in the book.
Practice Threading Exercises
Practicing threading exercises will help solidify your understanding of race conditions, deadlocks, and other common multi-threading problems.
Show steps
  • Implement solutions to common threading problems like the producer-consumer problem.
  • Debug and test your code thoroughly to identify and fix any concurrency issues.
Four other activities
Expand to see all activities and additional details
Show all seven activities
Document Threading Concepts
Creating documentation on threading concepts will reinforce your understanding and help you explain these concepts to others.
Show steps
  • Choose a specific threading concept, such as race conditions or deadlocks.
  • Write a clear and concise explanation of the concept, including code examples.
  • Share your documentation with others and solicit feedback.
Review 'Concurrency in C# Cookbook, Second Edition'
This book provides practical solutions to common concurrency problems in C#, reinforcing the course material.
Show steps
  • Read the recipes related to the specific threading challenges you are facing.
  • Adapt the code examples to your own projects.
Build a Multi-Threaded Application
Building a multi-threaded application will provide hands-on experience with applying the concepts learned in the course.
Show steps
  • Choose a project that can benefit from multi-threading, such as image processing or data analysis.
  • Design the application architecture and identify the parts that can be parallelized.
  • Implement the multi-threading logic using C# threading primitives.
  • Test and optimize the application for performance and stability.
Help Others with Threading Questions
Mentoring others on threading concepts will solidify your understanding and help you identify any gaps in your knowledge.
Show steps
  • Participate in online forums or communities related to C# and multi-threading.
  • Answer questions from other developers and provide helpful guidance.

Career center

Learners who complete How To Write Bulletproof Multi-Threaded C# Code will develop knowledge and skills that may be useful to these careers:
High Frequency Trading Programmer
High Frequency Trading Programmers develop software for automated trading systems that require extremely low latency and high throughput. This course provides the knowledge necessary to write robust multi-threaded C# code, essential for handling concurrent market data feeds and executing trades with minimal delay. High Frequency Trading Programmers require understanding how to avoid race conditions, deadlocks, and synchronization issues to ensure the reliability of trading systems. Learning about the reasons multi-threaded code crashes and how to resolve race conditions is useful for High Frequency Trading Programmers.
Real-Time Systems Programmer
Real Time Systems Programmers develop software that must respond to events within strict time constraints, often requiring precise multi-threading and synchronization. This course provides a solid background for writing robust multi-threaded C# code, which is essential for building reliable real-time systems. Real Time Systems Programmers understand the importance of avoiding race conditions, deadlocks, and synchronization issues to guarantee timely responses. Learning to resolve race conditions, use AutoResetEvent and ManualResetEvent, and line up threads with the Barrier class is very important.
Parallel Computing Specialist
A Parallel Computing Specialist focuses on developing algorithms and software that can effectively utilize parallel processing architectures. This course helps build a deep understanding of multi-threading in C#, which is crucial for maximizing the performance of parallel applications. This role typically requires a master's degree, or a doctorate. Parallel Computing Specialists need to know how to avoid race conditions, deadlocks, and synchronization issues when designing parallel algorithms. Learning to safely abort a thread, exchange data between two or more threads, and line up threads with the Barrier class is very important.
Distributed Systems Engineer
Distributed Systems Engineers design, develop, and maintain large-scale distributed systems, often involving complex multi-threading and concurrency patterns. This course helps build the skills necessary to write robust multi-threaded C# code, which is essential for building scalable and reliable distributed applications. Distributed Systems Engineers understand the importance of avoiding race conditions, deadlocks, and synchronization issues across multiple nodes. Learning to safely abort thread or exchange data between two or more threads, and line up threads with the Barrier class is very important.
Systems Programmer
Systems Programmers work on the core components of operating systems and system-level software, often requiring deep understanding of concurrency and multi-threading. This course can help in gaining expertise in writing robust multi-threaded C# code, essential for optimizing system performance and ensuring stability. Systems Programmers benefit from learning to resolve race conditions, deadlocks, and other synchronization issues, as well as understanding how to safely manage threads. Learning the many reasons why multi-threaded code crashes, using AutoResetEvent and ManualResetEvent, and understanding the Dining Philosopher problem would be useful to a Systems Programmer.
Technical Lead
A Technical Lead guides a team of developers, making key architectural decisions and ensuring code quality. This course helps build a strong understanding of multi-threading in C#, enabling you to guide your team in writing robust, high-performance code. Technical Leads benefit from knowing how to avoid race conditions, deadlocks, and synchronization issues, and how to effectively manage threads in complex projects. Learning to safely abort a thread, exchange data between two or more threads, and use AutoResetEvent and ManualResetEvent would be useful.
Software Developer
A Software Developer designs, develops, and tests software applications, often working with multiple programming languages and frameworks. This course helps build a foundation for writing robust multi-threaded C# code, a critical skill when building high-performance applications, APIs, or services that handle concurrent requests. Software Developers learn to resolve race conditions, deadlocks, and synchronization issues, ensuring code stability and reliability. Learning to safely exchange data between threads, use AutoResetEvent and ManualResetEvent, and implement thread rendezvous with the Barrier class this course may be useful to a Software Developer.
Application Architect
An Application Architect is responsible for designing the structure and behavior of software applications, ensuring they meet business requirements and are scalable. This course offers a comprehensive overview of multi-threading in C#, a crucial aspect of application performance and responsiveness. Application Architects must understand how to design applications that efficiently utilize threads, avoid common pitfalls like race conditions and deadlocks, and synchronize threads effectively. Learning to safely abort a thread, exchange data between two or more threads, and line up threads with the Barrier class may be useful to an Application Architect.
Cloud Engineer
Cloud Engineers design, deploy, and manage applications and services in cloud environments, often leveraging multi-threading to handle concurrent requests and optimize resource utilization. This course helps in mastering the art of writing robust multi-threaded C# code, which is critical for building scalable and performant cloud applications. Cloud Engineers understand the importance of avoiding race conditions, deadlocks, and synchronization issues when building cloud-native solutions. Learning to resolve race conditions, safely abort a thread, and line up threads with the Barrier class would be useful to a Cloud Engineer.
Game Developer
Game Developers create video games, often employing multi-threading to manage complex game logic, rendering, and physics simulations. This course helps build the skills needed to write robust multi-threaded C# code, essential for creating smooth and responsive game experiences. Game Developers understand the importance of avoiding race conditions, deadlocks, and synchronization issues, along with efficiently managing threads. Learning to resolve race conditions, safely abort a thread, and understand the Dining Philosopher problem this course helps aspiring Game Developers.
Embedded Systems Engineer
Embedded Systems Engineers design and develop software for embedded systems, which often have limited resources and require efficient use of multi-threading. This course helps master the skills needed to write robust multi-threaded C# code, which is valuable for optimizing performance on embedded platforms. Embedded Systems Engineers understand the importance of avoiding race conditions, deadlocks, and synchronization issues when working with embedded systems. Understanding the reasons why multi-threaded code crashes and learning to resolve race conditions is useful.
Financial Software Developer
A Financial Software Developer creates and maintains software applications for the financial industry, often dealing with complex calculations and real-time data processing. This course may be useful in writing robust multi-threaded C# code, essential for building scalable and reliable financial systems. Financial Software Developers understand the importance of avoiding race conditions, deadlocks, and synchronization issues to ensure data integrity and accuracy. Learning to resolve race conditions, use AutoResetEvent and ManualResetEvent, and learn the many reasons why multi-threaded code crashes would be useful.
Database Administrator
A Database Administrator manages and maintains databases, ensuring data integrity, performance, and security. This course may be useful when dealing with database systems that utilize multi-threading to handle concurrent requests and optimize query processing. Database Administrators need to understand how multi-threading works to troubleshoot performance bottlenecks, resolve deadlocks, and ensure data consistency. Learning to resolve race conditions; use AutoResetEvent and ManualResetEvent; and understand when multi-threaded code crashes this course could be useful to database administrators.
Kernel Developer
Kernel Developers work on the core of operating systems, writing low-level code that manages hardware and system resources. This course may be useful in understanding multi-threading concepts in C#, which is important for optimizing kernel performance and managing concurrency. Kernel Developers need to be aware of race conditions, deadlocks, and synchronization issues to maintain system stability. Learning about the many reasons why multi-threaded code crashes and how to resolve race conditions is useful.
Compiler Engineer
A Compiler Engineer designs and implements compilers, which translate high-level programming languages into machine code. This course may be useful for understanding multi-threading concepts in C#, which can be applied to optimize compiler performance and handle concurrent compilation tasks. Compiler Engineers need to be aware of race conditions, deadlocks, and synchronization issues to ensure correct code generation. Learning about the main reasons why multi-threaded code crashes may be useful.

Reading list

We've selected two books that we think will supplement your learning. Use these to develop background knowledge, enrich your coursework, and gain a deeper understanding of the topics covered in How To Write Bulletproof Multi-Threaded C# Code.
Provides practical solutions to common concurrency problems in C#. It's a valuable resource for understanding different threading patterns and best practices. This book adds more depth to the existing course by providing real-world examples and code snippets. It useful reference tool for developers working on multi-threaded applications.
Provides a comprehensive overview of C# and .NET Core, including sections on asynchronous programming and multi-threading. It's a useful reference for understanding the underlying platform and language features used in the course. While not solely focused on multi-threading, it provides valuable context and background knowledge. This book is commonly used as a textbook at academic institutions.

Share

Help others find this course page by sharing it with your friends and followers:

Similar courses

Similar courses are unavailable at this time. Please try again later.
Our mission

OpenCourser helps millions of learners each year. People visit us to learn workspace skills, ace their exams, and nurture their curiosity.

Our extensive catalog contains over 50,000 courses and twice as many books. Browse by search, by topic, or even by career interests. We'll match you to the right resources quickly.

Find this site helpful? Tell a friend about us.

Affiliate disclosure

We're supported by our community of learners. When you purchase or subscribe to courses and programs or purchase books, we may earn a commission from our partners.

Your purchases help us maintain our catalog and keep our servers humming without ads.

Thank you for supporting OpenCourser.

© 2016 - 2025 OpenCourser