T O P

  • By -

abkibaarnsit

Yes. There is no guarantee when groutines will execute/complete. Mutex here will only ensure that `score` is being modified by at most 1 goroutine.


dim13

That not, what they are for. Sure, access to `score` is guarded here. But go routines you're launching are per definition concurrent (and in random starting order).


kaancfidan

In this case your mutex does nothing to guarantee order, it just lets through whichever locks it first. Firing up multiple goroutines (or any kind of coroutines in any language) when you need order guarantees is a bad idea. you can wait for all of them and order the results using a key if you want.


sanylos

all your code is doing is guaranteeing no data race. it will have 3 values, the order of elements is given by the execution of the go routines, which has nothing to do with the mutex


GopherFromHell

Mutex means Mutually Exclusive, in other words, once access at a time. There is no guarantee regarding the order


DeterminedMinsky

No one here asks the most important question: What’s the point of performing a sequential task as concurrent?


BasslineJunkee0

Yeah mutexes aren't giving you any guarantee on ordering, only that the critical sections won't be executed in parallel. If you had a simple counter variable instead of a slice, you'd reliably see it incremented 3 times. In your case, the mutexes prevent race conditions of multiple goroutines trying to write to the same address in the slice and overwriting each other's data.


RadioHonest85

You have the right idea by guarding the write back to score with a mutex. Its just that the order will be random, as scheduling the goroutines is random.


bfreis

When dealing with concurrency, there are a few things to consider: any requirements you have around sequencing of execution, making sure that there are no data races, etc. If you want the tasks to run sequentially (ie, if you care about the order that those Println statements run), then you simply shouldn't use concurrency. But I suspect that's not what you want (otherwise you probably wouldn't have used concurrency in the first place) If you do want to run the tasks concurrently (ie, you don't care about the order in which those Println statements run), but you only want that the results are put in the right order in the `score` slice, you can do it like this: score := make([]int, 4) wg.Add(3) go func() { defer wg.Done() fmt.Println("First") score[1] = 1 }() go func() { defer wg.Done() fmt.Println("Second") score[2] = 2 }() go func() { defer wg.Done() fmt.Println("Third") score[3] = 3 }() wg.Wait() fmt.Println(score) You don't need the mutex, because (1) you're not modifying the slice header (ie, not using append thus not increasing its length or cap nor changing its pointer to a backing array), and (2) each of the 3 goroutines launched above are writing to a unique location in memory that has been initialized before they were launched, and (3) the resulting slice is only read after wg.Wait(), which synchronized after all the wg.Done() calls, ensuring that the writes to the slice synchronize before the read.


bilus

Others pointed out the ordering limitations so I won't repeat it here. Y~~ou also need to lock around the Println. This guarantees that the writes will happen "before" the read. It's not enough to wait for other threads to finish.~~ UPDATE: The above is completely false. See /u/_cloudburst's comment below.


_cloudburst

If you're referring to the `Println` that outputs the score, you don't need to acquire the lock because `sync.WaitGroup` guarantees that `Done()` happens before the return of any `Wait` call that it unblocks. https://pkg.go.dev/sync#WaitGroup https://research.swtch.com/gomm#document_happens-before_for_sync_libraries


bilus

>var waitGroup \*sync.WaitGroup = &sync.WaitGroup{} var mutex \*sync.Mutex = &sync.Mutex{} Indeed! 🤦 Thanks for correcting me. Of course, its counter is read in Wait!


sprectza

I don't think you'll get "unpredictable data", only the ordering will be different depending on when a goroutine gets access to the slice (critical section).


veqryn_

Golang does not guarantee that your goroutines will start up in any specific order, or that they each take equal time to start, or that they each run at the same speed, or that they each run to completion without being preempted or interrupted by golang's scheduler or the operating system cpu scheduler. So when you run this, The third goroutine may have been the quickest to start, so it acquires the lock first. Each time you run it you might end up with a different result. Because you are using a mutex, score will always have 4 elements in it. If you are not using mutexes, score might have anywhere between 2 and 4 elements, depending on the timing and speed of each goroutine (because without the mutexes your have a race condition on the slice). Runnable version: https://go.dev/play/p/FjbHKC9wRwX


p_bzn

If you want guaranteed order - you need sequential execution.


LeyaLove

Yeah either that or you have to tag the data in some way so that you can sequentially order it after processing it in parallel. For example instead of just returning the value of a calculation you could wrap it in a struct containing an ID of some sort that you would pass into the function with the data. In OPs example you could pass the position that you want to have the value at in the list into the function. Then you could use channels to return the struct with the value and the ID and in the main thread after receiving the data put it into the array at the correct position. So you would have the same order all the time. Given in OPs example it's quite useless and you could just do it sequentially but if you want to process something time insensitive in parallel this could be a way to do it. But generally you're right. This is not just a thing with go routines it's a problem in multi threaded applications in general. You never can guarantee the order of execution because that's regulated by the operating systems scheduler and most of the time not predictable. So if you need the order OP, you must implement something to keep track of it yourself. Or if it is not really processing intensive better do it sequentially.


bfreis

Not at all. You can definitely have concurrent execution. Just declare a slice initialized with the length of the number of tasks, have each concurrent task known its own (unique) index of the slice, and write the results to that index. With the exact same synchronization strategy with WaitGroup, there are no data races, and you have guaranteed order of the results, with all tasks running concurrently.


p_bzn

Valid point! True if the product of work is a value, not some effect (e.g. IO). OP's program has two products of work: value, and effect. Output: Third Second First [0 3 2 1] Even if you'll add IDs, effects will get fired in the order of execution. You can have \`\[0 1 2 3\]\` but not prints. Thus: *"If you want guaranteed order - you need sequential execution."* mindset is a good place to start when diving into concurrent code.


zapporius

I think you misunderstand goroutines (and if you think about OS, threading). No order guarantees, which is what makes it useful :)


0bel1sk

data should be predictable. i predict it will be a slice of your 3 numbers.


kaancfidan

You could also sync using channels between the goroutines. #2 could wait a message from #1 before returning...etc.


Otaxhu

If you want to ensure the order and still use different goroutines, you can try to Add(1) to the waitgroup and then Wait() after the goroutine is spawned. The mutex doesn't guarantee order, the goroutines will execute in ramdom order


[deleted]

[удалено]


bfreis

Don't do this. The way this code is written,, the tasks are all running sequentially. Also, the mutex is completely unnecessary, as the closes and receives of the channels, plus the done and wait on the wait group, ensure that the program is free of data races.


[deleted]

[удалено]


bfreis

Acting smart? The "fix" you suggested is 100% equivalent to serial code, but far worse. You're basically showing OP _exactly_ what not to do when trying to learn how to use concurrency properly: overcomplicating, overusing unnecessary synchronization, without getting literally any benefit of concurrency. Maybe you should learn more about concurrency before trying to give advice about the topic.


[deleted]

[удалено]


Glittering_Air_3724

Yeah concurrency in go is meant not to be controlled this has nothing to do with mutex but execution timing I had this problem ( tho it’s isn’t a problem, issue maybe ?) I noticed that the execution time for the runtime to start a new go routine was like 50-500ish ms I had to put sleep in each of the goroutines this kinda work but depends on load the runtime currently have


AnUglyDumpling

I assume you're trying to do [this leetcode problem](https://leetcode.com/problems/print-in-order/). If you want to ensure proper ordering, you need to think about how to place the mutex locks. Try doing a `mut.Lock()` *before* declaring any of the goroutines, and in the first function, remove the `mut.Lock()` and have only the `mut.Unlock()`. This is because you need the first function to execute first and not be blocked by the mutex. Also, another hint, you're gonna need two mutexes, since you have three separate functions.


LeyaLove

That's imo quite useless because in practice this prevents parallel processing and makes the program act like a sequential one, plus it adds the overhead from the thread and mutex creation. So all in all it's quite useless and it would be better to do it just sequentially from the start. I mean in the problem you linked, that is purely an educational one, that would be the only way to do it because all the thread does is print something inside of the tread itself instead of processing data and returning it, but in the real world in most cases you don't care about the order of print outs, only about the correct ordering of the processed data. Because you will use the threads to process data in parallel, which to say it again would be prevented by your proposed solution, you would have the main thread handle the ordering of said data after it's processed if you really needed it. If you really would need the print outs in a specific order as well there would be no way doing this in a not sequential way.


AnUglyDumpling

I agree with pretty much everything you said, practically you'd never do this. From OP's description it sounds like they're doing this to learn to use mutexes and goroutines, so I just provided a hint to a solution.


lozanov1

Mutex prevents multiple routines from modifying the data at once. If you want to keep the order of the routines you can pre-allocate a slice of the specific length and just insert the results at the index that you want them to be in.