DEV Community

DEV Community

Kittipat.po

Posted on Aug 26, 2023

Concurrency in Go: A Practical Guide with Hands-On Examples

Introduction.

Navigating the realm of concurrency in programming can be like orchestrating a symphony of activities that occur simultaneously. Enter the world of Go, a programming language that gracefully handles concurrency through goroutines and channels. In this blog, we'll journey through a series of hands-on examples, each illustrating an essential lesson in harnessing the power of concurrency in Go.

Lesson 1: Starting with the Basics

In our first lesson, we get acquainted with the concept of concurrency. The function infiniteCount continuously prints out numbers along with a given text, providing us with a simple example of a recurring task. However, the call to infiniteCount("cat") never occurs after starting with infiniteCount("dog") , highlighting how concurrency can affect program execution.

Lesson 2: Introducing Goroutines

In lesson two, we dive into goroutines , Go's concurrency units. By using the go keyword before a function call, we're able to launch concurrent execution. We start counting "dog" concurrently and immediately proceed to counting "cat". This showcases the non-blocking nature of goroutines.

Lesson 3: The Challenge of Asynchrony

Expanding on the previous lesson, we explore the scenario where both infiniteCount("dog") and infiniteCount("cat") are run as goroutines. Surprisingly, the expected output doesn't appear. Why? Because the program's main function exits before the goroutines finish execution, leading to an incomplete run.

Lesson 4: Synchronization with WaitGroups

To address the synchronization challenge posed in the previous lesson, we introduce the sync.WaitGroup . This construct helps us ensure that all goroutines finish before the program terminates. With sync.WaitGroup , we can synchronize goroutines' execution, waiting for their completion using wg.Wait() .

Lesson 5: Communicating via Channels

Moving beyond isolated goroutines , we delve into channels , the communication mechanism between concurrent processes in Go . We modify the countWithChannel function to send messages through channels. In lesson5, we create a channel, pass it to the function, and receive and print messages using a loop.

Lesson 6: Escaping Channel Deadlocks

Here, we confront the issue of channel deadlocks. While we might expect sending and receiving a message to work, we're greeted with a deadlock. Why? The send operation is blocking until there's a receiver. As there's no active receiver, the program is stuck in a deadlock.

Lesson 7: Buffers for Unblocking

To circumvent deadlocks, we introduce buffered channels. A buffered channel allows sending values without blocking until the buffer is full. In lesson7, we create a buffered channel with a capacity of 2, enabling us to send two messages without encountering a deadlock.

Lesson 8: Selecting from Channels

The select statement comes to the rescue in this lesson. We have two channels, c1 and c2 , each receiving messages at different intervals. By using select , we can non-blockingly choose whichever channel is ready to send data, showcasing the versatility of this construct.

Lesson 9: Building Worker Pools

Our final lesson delves into worker pools, a vital concept in concurrent programming. We define a worker function that processes jobs from a channel and sends results to another channel. In lesson9, we create a worker pool of three goroutines, distribute tasks, and collect results, effectively managing concurrent execution.

Conclusion 🥂

As you wrap up this exploration, you're now armed with the prowess of concurrency through Go's goroutines and channels. Whether it's juggling diverse tasks, seamlessly exchanging information, or optimizing work sharing, you've delved into the heart of concurrent programming. This newfound ability empowers you to enhance your code's performance, responsiveness, and efficiency.

Top comments (3)

pic

Templates let you quickly answer FAQs or store snippets for re-use.

psysolix profile image

  • Location Belgium
  • Education General UI Development / PHP / Java
  • Pronouns S P A C E W I Z A R D
  • Work Software Engineer
  • Joined Nov 25, 2021

Good practical examples and post! Thanks 🙏

rogudator profile image

  • Joined Aug 24, 2023

Your method of explaining is great!

zaynakbaarr profile image

  • Joined Sep 12, 2023

Thanks for the solution You can also visit this For more details.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink .

Hide child comments as well

For further actions, you may consider blocking this person and/or reporting abuse

hossamgouda profile image

Understanding the Abstract Factory Pattern: A Simple Guide🏭✨

Hossam Gouda - Sep 3

rsmacademybd profile image

JavaScript Event Delegation সম্পর্কে বিস্তারিত আলোচনা

RSM Academy BD - Sep 3

ohwow profile image

How to Detect VPNs with JavaScript

marwenshili profile image

Mastering Advanced React: Strategies for Efficient Rendering and Re-Rendering

Shili Mrawen - Sep 3

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Concurrency and Goroutines - Learn Parallelism in Golang

  • With Code Example
  • August 10, 2023
Learn concurrency in golang

Series - Golang Best Practices

  • 1: Introduction to Golang Best Practices
  • 2: Code Formatting and Style - A Guide for Developers
  • 3: How To Handle Errors In Golang Effectively
  • 4: Concurrency and Goroutines - Learn Parallelism in Golang
  • 5: Memory Management in Golang - Safeguarding Efficiency and Stability
  • 6: Testing, Benchmarking and Continuous Integration in Golang
  • 7: Performance Optimization in Golang
  • 8: Package and Module Design in Golang
  • 9: Security Best Practices for Go Applications
  • 10: Documentation and Comments in Go
  • 11: Debugging Techniques in Golang
  • 12: Continuous Improvement and Code Reviews
  • 13: Understanding Error Handing in Golang

Concurrency is a powerful aspect of modern programming that allows developers to handle multiple tasks simultaneously, making the most out of multi-core processors and enhancing the performance of applications. In Golang, concurrency is made simple and efficient with the concept of Goroutines. This article delves deep into the world of concurrency in Golang, covering the three main aspects - handling concurrency with Goroutines , synchronization with channels and mutexes, and best practices for managing Goroutine lifecycles. Throughout this journey, we will explore practical examples to understand these concepts better.

Handling Concurrency with Goroutines

Goroutines are lightweight threads that enable concurrent execution in Golang. Unlike traditional threads, Goroutines are managed by the Go runtime, making them highly efficient and scalable. Creating a Goroutine is as simple as using the go keyword followed by a function call.

Example - Goroutine for Concurrent Execution:

In this example, the printNumbers function runs concurrently as a Goroutine, printing numbers from 1 to 5. The main function continues executing independently, printing its numbers in parallel with the Goroutine. The use of time.Sleep ensures that the Goroutine has enough time to complete before the program exits.

Synchronization with Channels and Mutexes

Concurrency brings challenges of its own, such as race conditions and data races. To safely communicate and synchronize data between Goroutines, Golang provides channels and mutexes.

Channels are used for communication between Goroutines. They provide a safe and efficient way to send and receive data. Channels can be unbuffered or buffered, allowing for synchronous or asynchronous communication, respectively.

Example - Using Channels for Communication:

Mutexes are used to protect shared resources from concurrent access. They ensure that only one Goroutine can access a shared resource at a time, preventing data races and maintaining data integrity.

Example - Using Mutexes for Synchronization:

In this example, we use a mutex to protect the shared counter variable from concurrent access by multiple Goroutines.

Best Practices for Managing Goroutine Lifecycles

Managing Goroutine lifecycles is crucial to avoid resource leaks and ensure that Goroutines terminate gracefully. Best practices include using WaitGroups, channels, and context package to manage the lifecycle of Goroutines effectively.

Example - Using WaitGroups to Wait for Goroutines:

In this example, we use a WaitGroup to wait for the Goroutine to complete before proceeding with the main function.

Concurrency and Goroutines are powerful features in Golang that enable developers to harness the full potential of multi-core processors and achieve impressive performance improvements in their applications. By understanding how to handle concurrency with Goroutines, synchronize data with channels and mutexes, and manage Goroutine lifecycles efficiently, developers can create highly efficient and robust concurrent applications. Golang’s simplicity and strong support for concurrency make it a great choice for building scalable and high-performance systems. As a Golang developer, mastering concurrency and Goroutines is a skill that can take your applications to the next level.

  • Golang best practices

Related Posts

Synchronization primitives in the sync package.

Concurrency is a fundamental aspect of modern software development, and Go, also known as Golang, provides a robust set of tools for concurrent programming.

Building a Golang Gin API with Google OAuth

Hello! In today’s article, we will explore how to use Google OAuth in your Gin application. To achieve this, we’ll leverage the github.

Testing and Debugging in GoLang Fiber

In the world of web development, the journey to creating robust applications involves mastering the arts of testing and debugging. GoLang Fiber , a powerful web framework, provides an environment conducive to effective testing and debugging practices.

Introduction to GoLang Fiber

In the ever-changing world of web development, it is crucial to pick the right framework . Every developer looks for fast, simple and comprehensive functionalities.

Golang Clean Code Guide - 2

In the intricate world of programming, functions are the building blocks that construct the edifice of your code. In this article, we embark on a journey to uncover the art of designing functions that are concise, coherent, and highly functional.

Mastering Structured Logging: A Comprehensive Guide

In today’s complex software ecosystems, application logs are invaluable. They allow developers to peek under the hood and uncover what’s really happening inside their systems.

Parallel processing in Go

Mug shot of Lammert Bies

Goroutines are a unique feature in the Go programming language. I warned you, this will not be a beginners tutorial, but one of the aspects in modern programming education which gets not enough attention IMHO is the parallel execution of multiple tasks within one program. The clock speed of consumer computers has been limited around a handful of GHz for some years now and physical limitations do no give hope that this will change anytime soon. To provide more processing power to end-users, CPU manufacturers started to glue multiple CPU cores on top of each other, providing more available compute cycles per second.

This is a nice approach if you are running many programs which are not CPU intensive. The CPU scheduler can distribute the individual tasks over the available CPU cores and everything runs very smoothly. But it becomes more difficult, if there is only one task which must be distributed over multiple cores. This can only be done if the program and algorithms in it have been designed for it. And that is often not the case.

The limits of sequential programming

Educational systems love a simple and deterministic approach. Exam questions have typically only one outcome for example. Programs running multiple independent parallel processes internally are not deterministic. Without special precautions, you don’t know which task is completed first and if dependencies between the tasks are properly met. Even important large scale software like the database system MySQL is running internally most tasks in only one process. Multiple requests can be distributed over multiple cores, but if you happen to be a single user on a MySQL server and running one large query, that query is not evenly distributed over the cores.

To be honest, it is not only the educational system which is to blame for this situation. Programming languages–with the notable exception of Ada–have never been designed for multi-tasking. Implementation has been sloppy at best with hardware and operating system dependent libraries for atomic operations, locking and thread management. It takes a lot of programming lines to spawn and control tasks in programming languages like C.

Go is different. Go was designed for multi-tasking. Routines can be started with the statement go . That is right, the statement to start an independent routine in Go is named after the language itself. That shows the importance of parallel processing in the mind of the designers of the language.

The Concept of Goroutines

Routines which run parallel tasks inside a Go program are called goroutines. Goroutines are just functions where the calling routine is continuing with next statements without waiting for the goroutine to complete. This is a nice concept, but once the goroutine is started, it gets out of sight. We won’t know if the routine is completed, needs some input or send some output or tries to access data which is also accessed by other routines. While starting a goroutine is simple, using it in the right way may not be that simple after all.

Our first Goroutine Example

Simple examples say often more than thousands of words. In this example, I will create a goroutine, start it twice, and then wait until both routines have completed.

This Go program simulates two athletes competing in a running race. After the GO! signal of the referee (no pun intended) both athletes start and need each a random amount of time to reach the finish. When both athletes have reached the finish, the program executes.

Even though this is just a small example program, it contains a lot of important concepts and I will step through them one by one.

Waiting a random time

A lot of code is consumed by having each athlete using a random time between start and finish. Random functionality is provided in the package rand, but we have to do some initialization and data conversion before random data can be used to feed a timer. Let’s first dig through the random generator code. We’ll tackle the parallel programming afterwards.

As most compiler provided software random generators, the generator in Go provided by the math/rand package is a pseudo-random generator where the result of the algorithm is not obvious, but still deterministic. Running the our program multiple times would result in the same outcome. Fortunately the package provides a rand.Seed() function which allows us to change the starting point of the generator. We’ll feed it with the current time in nano seconds. There is not much chance this will result in the same outcome of the program when it is run multiple times.

Inside the athlete() function, we will wait a random time between 0 and 10 seconds between start and finish. Now it is becoming interesting, because many languages allow us to feed the integer outcome of a random generator directly to a timer function. Not in Go which has a very strong type-check functionality. Duration time values in Go are internally stored as int64 . representing an elapsed time in nanoseconds. But we cannot just assign an integer value to it. We have to convert it with time.Duration() , and then multiply it with time.Millisecond() to feed the time.Sleep() function. I admit, there are much shorter ways in C to do it, but when reading this code after 10 years, you will still be able to figure out what is going on. With a piece of C code, you will probably be lost re-reading your code again after some time has passed.

Note that timing is important in the type of programming I usually do, and the time functions are covered in other parts of this tutorial. So don’t panic if you have still doubts about how time functions in Go work.

Calling parallel functions, waiting for completion

The main goal of the example program is explaining the parallel execution of functions. Two athletes are competing in our running race, John and Jane. They both start running at roughly the same time after the GO! command of the starter. The moment they will finish is however not determined yet when their function is called. But we would like to end the program only after both runners have passed the finish line. A simple sleep timer with a value longer than the maximum time produced by the random generator could be used but it has two problems. First of all, if the running times of the athletes are much shorter than the maximum time, we will wast time waiting for something which has already happened. If on the other hand the time needed for an athlete function is close to the maximum and some other processing in the Go program happens which delays the execution of the last parts of the athlete function, we might stop the program before the finish message of the athlete functions is displayed. Remember, Go has a lot of processing going on over which we have only limited control like their garbage collector. That may not be a problem in a small example like this, but in a more complex environment random delays caused by these processes may cause severe problems.

Waiting for goroutines with the sync package

We will therefore add the functionality to wait for the goroutines to complete by using the sync package. This package has counters which count how many goroutines are active. If the number is down to zero, it is save to exit the main program.

Initializing the number of running goroutines

The first thing we have to do is define a wait group. This is a group of processes we want to wait for. We first tell the wait group that we will launch 2 go routines by calling wg.Add(2) . It is not necessary that we declare the total number of goroutines to wait for before any goroutine is started. It is also fine to call wg.Add(1) every time just before a goroutine is started. This can be a better approach, when for example starting goroutines in a loop where you don’t know the total amount of running goroutines beforehand.

The only thing which is important though is that you call the sync.Add() method before the corresponding goroutine is actually launched. The reason is that we must be absolutely sure that the sync.Add() method is called before the sync.Done() method is used inside the goroutine.

Telling a goroutine finished

The sync.Done() method must be called inside the goroutine when the function is finished. It is tempting to put it at the end of all the processing statements just before the closing curly bracket of the athlete() function. But this can lead to difficult to detect problems if the function gets more complex and has multiple exit pathways. We could end up in some rare condition where the function ends, but never calls sync.Done() . And trust me, I have been in this situation before with C and inter-process locking mechanisms. It is a horror-situation to debug. Normal debugging software is fine for sequential process flows, but once you have some hands full of parallel processes running in real-time, you are in uncharted territory. Only luck, or sometimes completely rewriting a routine from scratch can fix these errors.

Luckily the designers of Go have found a very elegant way to cope with this problem with the defer statement. Basically, defer is the equivalent of the on_exit() function in C, but with the important difference that it does not only work in the function main() , but in all functions in which it is called. It defines which actions have to be taken when a routine ends, without the need to be explicit about the reason why the routine ended. In our case, when the athlete() function ends through the normal flow of the code or through some error, the sync.Done() method is called before the goroutine completely seizes to exist.

Waiting for full completion of all goroutines

In our main routine we use the method sync.Wait() to wait for the two started goroutines to be completed. After that we put a message on the screen, and end the execution.

  • Is CS 121 a good fit for you?
  • Academic Honesty
  • Module Readings
  • M1: Introduction to Parallel Programming & Golang
  • M2: Shared Memory Architectures
  • M3: Principles of Mutual Exclusion
  • M4: Concurrent Objects (Part 1)
  • M5: Concurrent Objects (Part 2)
  • M6: Concurrent Execution Models
  • M7: Advanced Parallel Scheduling Techniques (Part 1)
  • M8: Advanced Parallel Scheduling Techniques (Part 2)
  • M9: GPGPU Programming & Parallel Programming in C & Python
  • Assignments
  • Office Hours
  • Asking Questions on Ed
  • UChicago CS Student Resource Guide
  • Running Go Remotely

M1: Introduction to Parallel Programming & Golang ¶

This first module provides an introduction to the course and parallel programming. During this module you will also get familiar with the language for this course, Golang.

Pre-recorded Lectures ¶

Note: The pre-recorded videos for M1 will be posted after Tuesday’s lecture .

The pre-recorded lectures are available here: M1 Videos . You can also find the videos under the “Panopto” tab on the MPCS 52060 canvas site.

The lectures are a series of approx 20-30 minute videos divided into the following sections:

Golang Code Structure & Tips <- If you already know Go then you can skip this video.

The slides presented in lecture and these videos are accessible on our Canvas Page. Click on the Files link and you then can download the m1.zip file.

Resources/Readings ¶

For this module, your focus should be to learn the basic syntax of the Go language. Look at the Module 1 slides (slide 36) to know what constructs to focus on.

The slides and code presented in this module are accessible on our Canvas Page. Click on the Files link and you then can download the m1.zip file.

See M1 slides to know the links to click on.

Great resource for learning and experimenting with the language syntax

The Go Programming Language (textbook) provides a few chapters on the syntax of the language. This is not required but if you want a more in-depth description about language constructs then you should reference this book.

There will be no readings from the official textbook this week.

Synchronous Session (In-person Lecture) ¶

As a reminder here are the dates and times for the synchronous session for this module:

Section 1: Tuesday September 27th @ 12:30pm-2:20pm

Course Logistics

Introduction to Parallel Programming

Golang Overview [if time permits]

Assignment ¶

Assignments are always due on Wednesday evenings.

Homework #1 , due Wednesday October 5th at 11:59pm CDT

The Go Blog

Go concurrency patterns: pipelines and cancellation.

Sameer Ajmani 13 March 2014

Introduction

Go’s concurrency primitives make it easy to construct streaming data pipelines that make efficient use of I/O and multiple CPUs. This article presents examples of such pipelines, highlights subtleties that arise when operations fail, and introduces techniques for dealing with failures cleanly.

What is a pipeline?

There’s no formal definition of a pipeline in Go; it’s just one of many kinds of concurrent programs. Informally, a pipeline is a series of stages connected by channels, where each stage is a group of goroutines running the same function. In each stage, the goroutines

  • receive values from upstream via inbound channels
  • perform some function on that data, usually producing new values
  • send values downstream via outbound channels

Each stage has any number of inbound and outbound channels, except the first and last stages, which have only outbound or inbound channels, respectively. The first stage is sometimes called the source or producer ; the last stage, the sink or consumer .

We’ll begin with a simple example pipeline to explain the ideas and techniques. Later, we’ll present a more realistic example.

Squaring numbers

Consider a pipeline with three stages.

The first stage, gen , is a function that converts a list of integers to a channel that emits the integers in the list. The gen function starts a goroutine that sends the integers on the channel and closes the channel when all the values have been sent:

The second stage, sq , receives integers from a channel and returns a channel that emits the square of each received integer. After the inbound channel is closed and this stage has sent all the values downstream, it closes the outbound channel:

The main function sets up the pipeline and runs the final stage: it receives values from the second stage and prints each one, until the channel is closed:

Since sq has the same type for its inbound and outbound channels, we can compose it any number of times. We can also rewrite main as a range loop, like the other stages:

Fan-out, fan-in

Multiple functions can read from the same channel until that channel is closed; this is called fan-out . This provides a way to distribute work amongst a group of workers to parallelize CPU use and I/O.

A function can read from multiple inputs and proceed until all are closed by multiplexing the input channels onto a single channel that’s closed when all the inputs are closed. This is called fan-in .

We can change our pipeline to run two instances of sq , each reading from the same input channel. We introduce a new function, merge , to fan in the results:

The merge function converts a list of channels to a single channel by starting a goroutine for each inbound channel that copies the values to the sole outbound channel. Once all the output goroutines have been started, merge starts one more goroutine to close the outbound channel after all sends on that channel are done.

Sends on a closed channel panic, so it’s important to ensure all sends are done before calling close. The sync.WaitGroup type provides a simple way to arrange this synchronization:

Stopping short

There is a pattern to our pipeline functions:

  • stages close their outbound channels when all the send operations are done.
  • stages keep receiving values from inbound channels until those channels are closed.

This pattern allows each receiving stage to be written as a range loop and ensures that all goroutines exit once all values have been successfully sent downstream.

But in real pipelines, stages don’t always receive all the inbound values. Sometimes this is by design: the receiver may only need a subset of values to make progress. More often, a stage exits early because an inbound value represents an error in an earlier stage. In either case the receiver should not have to wait for the remaining values to arrive, and we want earlier stages to stop producing values that later stages don’t need.

In our example pipeline, if a stage fails to consume all the inbound values, the goroutines attempting to send those values will block indefinitely:

This is a resource leak: goroutines consume memory and runtime resources, and heap references in goroutine stacks keep data from being garbage collected. Goroutines are not garbage collected; they must exit on their own.

We need to arrange for the upstream stages of our pipeline to exit even when the downstream stages fail to receive all the inbound values. One way to do this is to change the outbound channels to have a buffer. A buffer can hold a fixed number of values; send operations complete immediately if there’s room in the buffer:

When the number of values to be sent is known at channel creation time, a buffer can simplify the code. For example, we can rewrite gen to copy the list of integers into a buffered channel and avoid creating a new goroutine:

Returning to the blocked goroutines in our pipeline, we might consider adding a buffer to the outbound channel returned by merge :

While this fixes the blocked goroutine in this program, this is bad code. The choice of buffer size of 1 here depends on knowing the number of values merge will receive and the number of values downstream stages will consume. This is fragile: if we pass an additional value to gen , or if the downstream stage reads any fewer values, we will again have blocked goroutines.

Instead, we need to provide a way for downstream stages to indicate to the senders that they will stop accepting input.

Explicit cancellation

When main decides to exit without receiving all the values from out , it must tell the goroutines in the upstream stages to abandon the values they’re trying to send. It does so by sending values on a channel called done . It sends two values since there are potentially two blocked senders:

The sending goroutines replace their send operation with a select statement that proceeds either when the send on out happens or when they receive a value from done . The value type of done is the empty struct because the value doesn’t matter: it is the receive event that indicates the send on out should be abandoned. The output goroutines continue looping on their inbound channel, c , so the upstream stages are not blocked. (We’ll discuss in a moment how to allow this loop to return early.)

This approach has a problem: each downstream receiver needs to know the number of potentially blocked upstream senders and arrange to signal those senders on early return. Keeping track of these counts is tedious and error-prone.

We need a way to tell an unknown and unbounded number of goroutines to stop sending their values downstream. In Go, we can do this by closing a channel, because a receive operation on a closed channel can always proceed immediately, yielding the element type’s zero value.

This means that main can unblock all the senders simply by closing the done channel. This close is effectively a broadcast signal to the senders. We extend each of our pipeline functions to accept done as a parameter and arrange for the close to happen via a defer statement, so that all return paths from main will signal the pipeline stages to exit.

Each of our pipeline stages is now free to return as soon as done is closed. The output routine in merge can return without draining its inbound channel, since it knows the upstream sender, sq , will stop attempting to send when done is closed. output ensures wg.Done is called on all return paths via a defer statement:

Similarly, sq can return as soon as done is closed. sq ensures its out channel is closed on all return paths via a defer statement:

Here are the guidelines for pipeline construction:

  • stages keep receiving values from inbound channels until those channels are closed or the senders are unblocked.

Pipelines unblock senders either by ensuring there’s enough buffer for all the values that are sent or by explicitly signalling senders when the receiver may abandon the channel.

Digesting a tree

Let’s consider a more realistic pipeline.

MD5 is a message-digest algorithm that’s useful as a file checksum. The command line utility md5sum prints digest values for a list of files.

Our example program is like md5sum but instead takes a single directory as an argument and prints the digest values for each regular file under that directory, sorted by path name.

The main function of our program invokes a helper function MD5All , which returns a map from path name to digest value, then sorts and prints the results:

The MD5All function is the focus of our discussion. In serial.go , the implementation uses no concurrency and simply reads and sums each file as it walks the tree.

Parallel digestion

In parallel.go , we split MD5All into a two-stage pipeline. The first stage, sumFiles , walks the tree, digests each file in a new goroutine, and sends the results on a channel with value type result :

sumFiles returns two channels: one for the results and another for the error returned by filepath.Walk . The walk function starts a new goroutine to process each regular file, then checks done . If done is closed, the walk stops immediately:

MD5All receives the digest values from c . MD5All returns early on error, closing done via a defer :

Bounded parallelism

The MD5All implementation in parallel.go starts a new goroutine for each file. In a directory with many large files, this may allocate more memory than is available on the machine.

We can limit these allocations by bounding the number of files read in parallel. In bounded.go , we do this by creating a fixed number of goroutines for reading files. Our pipeline now has three stages: walk the tree, read and digest the files, and collect the digests.

The first stage, walkFiles , emits the paths of regular files in the tree:

The middle stage starts a fixed number of digester goroutines that receive file names from paths and send results on channel c :

Unlike our previous examples, digester does not close its output channel, as multiple goroutines are sending on a shared channel. Instead, code in MD5All arranges for the channel to be closed when all the digesters are done:

We could instead have each digester create and return its own output channel, but then we would need additional goroutines to fan-in the results.

The final stage receives all the results from c then checks the error from errc . This check cannot happen any earlier, since before this point, walkFiles may block sending values downstream:

This article has presented techniques for constructing streaming data pipelines in Go. Dealing with failures in such pipelines is tricky, since each stage in the pipeline may block attempting to send values downstream, and the downstream stages may no longer care about the incoming data. We showed how closing a channel can broadcast a “done” signal to all the goroutines started by a pipeline and defined guidelines for constructing pipelines correctly.

Further reading:

  • Go Concurrency Patterns ( video ) presents the basics of Go’s concurrency primitives and several ways to apply them.
  • Advanced Go Concurrency Patterns ( video ) covers more complex uses of Go’s primitives, especially select .
  • Douglas McIlroy’s paper Squinting at Power Series shows how Go-like concurrency provides elegant support for complex calculations.

Navigation Menu

Search code, repositories, users, issues, pull requests..., provide feedback.

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly.

To see all available qualifiers, see our documentation .

  • Notifications You must be signed in to change notification settings

A golang parallel library, used for business logic aggregation and refactory without changing user function declaration.

buptmiao/parallel

Folders and files.

NameName
10 Commits

Repository files navigation

Build Status

A golang parallel library, used for business logic aggregation and refactory without changing function declaration.

There are three methods: testjobA, testjobB, testjobC, execute them in parallel:

Let's see a little complex case, there are three parallel jobs: jobA, jobB, jobC and a final Job which aggregates the result. The final depends on jobA and middle which depends on jobB and jobC.

Refer to the demo below:

By default, Parallel will ignore panics of jobs. But parallel supports customized exception handler, which is used for dealing with unexpected panics. For example, alerting or logging.

more examples

Contributors 2.

  • Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers
  • Advertising & Talent Reach devs & technologists worldwide about your product, service or employer brand
  • OverflowAI GenAI features for Teams
  • OverflowAPI Train & fine-tune LLMs
  • Labs The future of collective knowledge sharing
  • About the company Visit the blog

Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Get early access and see previews of new features.

Multiple assignment by if statement

It is possible to execute multiple assignment by if condition, like the following code?

softshipper's user avatar

  • 3 Doesn't that look a little hard to decipher ? –  Denys Séguret Commented Aug 13, 2014 at 9:23
  • 3 I don't think so. Your code is a bit hard to read, too. Maybe you want to split it up into multiple if-statements. –  fuz Commented Aug 13, 2014 at 9:24
  • ok, I think it would be better for read too. –  softshipper Commented Aug 13, 2014 at 9:24

2 Answers 2

It looks like you want something like this:

peterSO's user avatar

No. Only one 'simple statement' is permitted at the beginning of an if-statement, per the spec .

The recommended approach is multiple tests which might return an error, so I think you want something like:

Bryan's user avatar

Your Answer

Reminder: Answers generated by artificial intelligence tools are not allowed on Stack Overflow. Learn more

Sign up or log in

Post as a guest.

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .

Not the answer you're looking for? Browse other questions tagged go or ask your own question .

  • The Overflow Blog
  • The hidden cost of speed
  • The creator of Jenkins discusses CI/CD and balancing business with open source
  • Featured on Meta
  • Announcing a change to the data-dump process
  • Bringing clarity to status tag usage on meta sites
  • What does a new user need in a homepage experience on Stack Overflow?
  • Feedback requested: How do you use tag hover descriptions for curating and do...
  • Staging Ground Reviewer Motivation

Hot Network Questions

  • In roulette, is the frequency of getting long sequences of reds lower than that of shorter sequences?
  • Microsoft SQL In-Memory OLTP in SQL Express 2019/2022
  • How to Interpret Statistically Non-Significant Estimates and Rule Out Large Effects?
  • Dimension from Hilbert series with variable-weighted grading?
  • In Lord Rosse's 1845 drawing of M51, was the galaxy depicted in white or black?
  • Does the USA plan to establish a military facility on Saint Martin's Island in the Bay of Bengal?
  • How can I play MechWarrior 2?
  • In macro "@k", using ^M at end of "call function()" executes the function, but also undesirably adds a new line to my text
  • Cohomological range of a perverse sheaf
  • Is it a good idea to perform I2C Communication in the ISR?
  • Could an empire rise by economic power?
  • Using rule-based symbology for overlapping layers in QGIS
  • What was the first "Star Trek" style teleporter in SF?
  • Checklist of documents for visiting Poland if I already have a Schengen visa
  • Does the average income in the US drop by $9,500 if you exclude the ten richest Americans?
  • Largest number possible with +, -, ÷
  • Long and protected macros in LaTeX3
  • Does a party have to wait 1d4 hours to start a Short Rest if no healing is available and an ally is only stabilized?
  • How do you tip cash when you don't have proper denomination or no cash at all?
  • Why doesn’t dust interfere with the adhesion of geckos’ feet?
  • Does it make sense for the governments of my world to genetically engineer soldiers?
  • If a Palestinian converts to Judaism, can they get Israeli citizenship?
  • Is it safe to install programs other than with a distro's package manager?
  • A bijection of a connected graph that preserves 2-distances but is not an automorphism

golang parallel assignment

IMAGES

  1. Golang’s golden pattern of parallel computing

    golang parallel assignment

  2. Golang Variables Declaration, Assignment and Scope Tutorial

    golang parallel assignment

  3. Golang unit testing

    golang parallel assignment

  4. How Concurrency and Parallelism works in Golang [Tutorial]

    golang parallel assignment

  5. golang: (concurrent/parallel) Network Automation using SSH pt.2

    golang parallel assignment

  6. Optimizing parallel Merge Sort with Golang tools

    golang parallel assignment

VIDEO

  1. Threading Simplified in 3 Minutes!

  2. PARALLEL ACTION

  3. LPV assignment Parallel bubblesort and mergesort using openmp

  4. Assignment #5

  5. Advanced Unit Testing Techniques In Golang-Mock, Subtests, Parallel Execution of Tests with hands-on

  6. Assignment 9: Parallel Hill Climber

COMMENTS

  1. go

    Regarding GOMAXPROCS, you can find this in Go 1.5's release docs:. By default, Go programs run with GOMAXPROCS set to the number of cores available; in prior releases it defaulted to 1. Regarding preventing the main function from exiting immediately, you could leverage WaitGroup's Wait function.. I wrote this utility function to help parallelize a group of functions:

  2. Go Multitasking: Comparing concurrency and parallelism in Go

    Oct 24, 2023. 1. Put simply, concurrency means handling many tasks together, while parallelism involves doing many tasks simultaneously. Another key point is that concurrency occurs on a single ...

  3. ExaScience/pargo: A library for parallel programming in Go

    A library for parallel programming in Go. Package pargo provides functions and data structures for expressing parallel algorithms. While Go is primarily designed for concurrent programming, it is also usable to some extent for parallel programming, and this library provides convenience functionality to turn otherwise sequential algorithms into ...

  4. How To Run Multiple Functions Concurrently in Go

    From the projects directory, use the mkdir command to create the program's directory (multifunc) and then navigate into it: mkdir multifunc. cd multifunc. Copy. Once you're in the multifunc directory, open a file named main.go using nano, or your favorite editor: nano main.go. Copy.

  5. Concurrency in Go: A Practical Guide with Hands-On Examples

    Output. ~ go run . 1 dog. 2 dog. 3 dog. 4 dog. In our first lesson, we get acquainted with the concept of concurrency. The function infiniteCount continuously prints out numbers along with a given text, providing us with a simple example of a recurring task. However, the call to infiniteCount("cat") never occurs after starting with ...

  6. Concurrency and Goroutines

    Concurrency is a powerful aspect of modern programming that allows developers to handle multiple tasks simultaneously, making the most out of multi-core processors and enhancing the performance of applications. In Golang, concurrency is made simple and efficient with the concept of Goroutines. This article delves deep into the world of ...

  7. Parallel processing in Go

    Parallel processing in Go. About the Author: Lammert Bies is a dad, husband and polyglot. He is developing embedded systems since the eighties. Used machine learning before it had a name. Specializes in interconnecting computers, robots and humans. Was a Google Mapmaker Advocate and speaker on several international Google conferences from 2011 ...

  8. Effective Go

    Prefix a function or method call with the go keyword to run the call in a new goroutine. When the call completes, the goroutine exits, silently. (The effect is similar to the Unix shell's & notation for running a command in the background.) go list.Sort() // run list.Sort concurrently; don't wait for it.

  9. M1: Introduction to Parallel Programming & Golang

    Note: The pre-recorded videos for M1 will be posted after Tuesday's lecture. The pre-recorded lectures are available here: M1 Videos . You can also find the videos under the "Panopto" tab on the MPCS 52060 canvas site. The lectures are a series of approx 20-30 minute videos divided into the following sections: Golang Code Structure & Tips ...

  10. Go Concurrency Patterns: Pipelines and cancellation

    The MD5All implementation in parallel.go starts a new goroutine for each file. In a directory with many large files, this may allocate more memory than is available on the machine. We can limit these allocations by bounding the number of files read in parallel. In bounded.go, we do this by creating a fixed number of goroutines for reading files ...

  11. Understanding Concurrency and Parallelism in Go (Golang)

    Differences: 1. Concurrency is about managing and organizing tasks, while parallelism is about executing tasks simultaneously to improve performance. 2. Concurrency doesn't guarantee that tasks ...

  12. Concurrency and Parallelism in Golang

    Concurrency is a property of a program where two or more tasks can be in progress simultaneously. Parallelism is a run-time property where two or more tasks are being executed simultaneously ...

  13. Concurrent API Calls and Race Conditions In Go

    Go was developed by Google to help make concurrent programming easier, safer and more efficient. Lewis Fairweather has written an in-depth article on Golang here which summarises the motivation behind Go, the advantages and drawbacks.. In this article, we will examine one of the greatest features of Go: it's support for concurrency.

  14. How to achieve proper parallelism in Golang? Are goroutines parallel in

    Every question I find on sites like StackOverflow appears to be outdated and doesn't take into account this change in version 1.5. See: Parallel processing in golang. My confusion arises from trying to test this parallelism in practice. I have tried the following code I in Go 1.10 and it doesn't run in parallel.

  15. go

    The one statement multiple assignment, which uses implicit temporary variables, is equivalent to (a shorthand for) the two multiple assignment statements, which use explicit temporary variables. Your fibonacci example translates, with explicit order and temporary variables, to: package main. import "fmt". func fibonacciMultiple() func() int {.

  16. GitHub

    A golang parallel library, used for business logic aggregation and refactory without changing user function declaration. - buptmiao/parallel. ... [string] int //assignment to entry in nil map a ["123"] = 1} func main { p:= parallel. NewParallel () p. Register (exceptionJob) // miss the last argument on purpose p. Except (exceptionHandler ...

  17. go

    First Go Routine finished before second Go Routine starts. To avoid concurrency in output you can use sleep before for statement. (Or use heavy non blocking task) - Sergey Krestov. Aug 21, 2017 at 7:25. Or you can make your output longer. (use 260 and 270 symbols instead 26 and 27). - Sergey Krestov.

  18. go

    No. Only one 'simple statement' is permitted at the beginning of an if-statement, per the spec. The recommended approach is multiple tests which might return an error, so I think you want something like: genUri := buildUri() if err := setRedisIdentity(genUri, email); err != nil {. return "", err.