Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>There are two different issues in your logic: the first one is how the slice itself is being manipulated, and the second one regards actual concurrency problems.</p> <p>For the slice manipulation, simply passing the slice by value as a parameter will mean that you won't be able to mutate the slice in a way that the call site will see it when the slice has to be grown or the backing array reallocated to accommodate the new data you're appending. There are two common ways to handle that.</p> <p>By returning the new slice:</p> <pre><code>func addWindow(windows []Window) []Window { return append(windows, Window{1, 1}) } room.Windows = addWindow(room.Windows) </code></pre> <p>Or by providing a mutable parameter that the call site maintains a reference to:</p> <pre><code>func addWindow(room *Room) { room.Windows = append(room.Windows, Window{1, 1}) } </code></pre> <p>For the second issue, you must make sure values are not being mutated concurrently in an unsafe way. There are many ways to address it as well:</p> <p><strong>Use a channel</strong></p> <p>Instead of a manipulating the room directly, you can ask windows to be produced by N goroutines, and have their results reported back to a non-racy control point. For example, you might have:</p> <pre><code>windows := make(chan Window, N) for i := 0; i &lt; N; i++ { go createWindow(windows) } for i := 0; i &lt; N; i++ { room.Windows = append(room.Windows, &lt;-windows) } </code></pre> <p>and <code>addWindow</code> would instead look similar to:</p> <pre><code>func createWindow(windows chan Window) { windows &lt;- Window{1, 1} } </code></pre> <p>This way the creation is concurrent, but the actual manipulation of the room is not.</p> <p><strong>Add a mutex field</strong></p> <p>It's also typical to have a private mutex field in the type itself, such as:</p> <pre><code>type Room struct { m sync.Mutex Windows []Window } </code></pre> <p>Then, whenever manipulating concurrency-sensitive fields, protect the exclusive area with the mutex:</p> <pre><code>room.m.Lock() room.Windows = append(room.Windows, window) room.m.Unlock() </code></pre> <p>Ideally the use of such a mutex should stay encapsulated close to the type itself, so it's easy to spot how it's being used. For that reason, you'll often see the mutex being used from within methods of the type itself (<code>room.addWindow</code>, for example).</p> <p>If you have panic-prone logic in the exclusive (protected) region, it may be a good idea to defer the <code>Unlock</code> call right after the <code>Lock</code> one. A lot of people simply put one straight after the other, even in simple operations, just so they don't have to figure whether it's safe or not to do so. That may well be a good idea if you're unsure.</p> <p><em>VERY IMPORTANT:</em> In most cases it's a bad idea to copy a struct with a mutex field by value. Instead, use a pointer to the original value. The reason for this is that internally the mutex relies on the address of its fields to not change for the atomic operations to work correctly.</p> <p><strong>Add a global mutex</strong></p> <p>In more unusual circumstances, which most probably do not apply for the case you're trying to handle, but which is good knowing about, you may choose to protect the logic itself instead of protecting the data. One way to do that is with a global mutex variable, with something around the lines of:</p> <pre><code>var addWindowMutex sync.Mutex func addWindow(room *Room) { addWindowMutex.Lock() room.Windows = append(room.Windows, Window{1, 1}) addWindowMutex.Unlock() } </code></pre> <p>This way <code>addWindow</code> itself is protected, no matter who is calling it. The advantage of that approach is that you don't depend on the implementation of room to do it. A disadvantage is that only a single goroutine will get into the exclusive region, no matter how many rooms are being processed in parallel (that's not the case with the prior solution).</p> <p>When doing this, remember that <em>reading</em> <code>room.Windows</code> or whatever data is being mutated in the exclusive region should also be protected, in case there's still concurrency going on to change it meanwhile.</p> <p>Finally, just as some unprompted feedback, do check those error values. Ignoring errors is a really bad practice, whether it's just an example or serious code. Many times you'll catch errors even when building up sample code like that.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload