Op zo 17 mrt. 2019 om 15:28 schreef Louki Sumirniy <louki.sumirniy.stal...@gmail.com>: > As my simple iterative example showed, given the same sequence of events, > channels are deterministic, so this is an approach that is orthogonal but in > the same purpose - to prevent multiple concurrent agents from desynchronising > shared state, without blocking everything before access. It's not a journal > but the idea is to have each goroutine acting on the final state of the value > at the time it is invoked to operate on it. So you let everyone at it but > everyone stops at this barrier and checks if anyone else changed it and then > they try again to have a conflict free access.
Channels are deterministic in the sense that if items A and B are enqueued in that order, they will be delivered in that order. They are not deterministic in two ways: if items A and B are enqueued concurrently, the order could be A B or B A; if two consumers of the channel read from it concurrently, it is not deterministic which will read. This doesn't seem to me to be related in any way to locking / mutual exclusion. The purpose of mutual exclusion is to serialize some execution history. In other words, the idea is to make an interleaved / concurrent execution history of some set of instructions impossible. That's not what a channel does. While a channel will act like a FIFO, when multiple concurrent producers and / or consumers are present, one cannot say which concurrent producer or consumer "wins" reading or writing. So this pattern is really only meaningful for SPSC workloads. What you term "access contention" seems to me somewhat analogous to "data race". Assuming "somestate" is a pointer to some shared mutable state, you might be able to determine whether that state changed after the channel operation, but: a) You're still susceptible to ABA problems when the state does not change deterministically. b) Even with a deterministically mutated state (such as a counter increasing by a constant factor), you still have TOCTOU (time-of-check versus time-of-use) issues. If your test of the state after the channel operation doesn't show that the state has changed, what guarantees that the state hasn't changed after your check? It's turtles all the way down from here. c) Both the assignment of "somestate" and a read from a channel create a copy of the thing that was yielded from the function or was sent over the channel, respectively. Unless that thing is a pointer to shared mutable state, that state can't be observed to change via external influence, anyway. The channel use you've demonstrated is much more analogous to a binary semaphore than any sort of lock in the sense that readers block on a binary condition (whether or not the channel contains a value). You can implement a mutex on a binary semaphore, but you need some notion of "lock" and "unlock". To achieve locking semantics with an unbuffered channel, the channel must begin in a non-empty state. When the channel is read from, the state transitions to "locked". When the lock is meant to be released, the releasing process writes to the channel. This is perilous: 1) if you ever accidentally write twice, you no longer have the guarantee of mutual exclusion, and 2) "unlocking" blocks until there's a a new process wishing to acquire the lock. You can solve the second point with a buffered channel with a capacity of 1, but you seem to be explicitly talking about unbuffered channels. The first point can be solved, but I don't think it's super relevant. If all you want to do is determine that some state was valid at the time it was accessed, atomics or mutexes are the correct approach. If you want to have multiple concurrent processes "agree" that they're both working with the same state for the entire duration of some execution history, you still need some sort of consensus protocol. --dho > On Sunday, 17 March 2019 20:52:12 UTC+1, Robert Engels wrote: >> >> https://g.co/kgs/2Q3a5n >> >> On Mar 17, 2019, at 2:36 PM, Louki Sumirniy <louki.sumir...@gmail.com> wrote: >> >> So I am incorrect that only one goroutine can access a channel at once? I >> don't understand, only one select or receive or send can happen at one >> moment per channel, so that means that if one has started others can't start. >> >> I was sure this was the case and this seems to confirm it: >> >> https://stackoverflow.com/a/19818448 >> >> https://play.golang.org/p/NQGO5-jCVz >> >> In this it is using 5 competing receivers but every time the last one in >> line gets it, so there is scheduling and priority between two possible >> receivers when a channel is filled. >> >> This is with the range statement, of course, but I think the principle I am >> seeing here is that in all cases it's either one to one between send and >> receive, or one to many, or many from one, one side only receives the other >> only sends. If you consider each language element in the construction, and >> the 'go' to be something like a unix fork(), this means that the first >> statement inside the goroutine and the very next one in the block where the >> goroutine is started potentially can happen at the same time, but only one >> can happen at a time. >> >> So the sequence puts the receive at the end of the goroutine, which >> presumably is cleared to run, whereas the parent where it is started is >> waiting to have a receiver on the other end. >> >> If there is another thread in line to access that channel, at any given time >> only one can be on the other side of send or receive. That code shows that >> there is a deterministic order to this, so if I have several goroutines >> running each one using this same channel lock to cover a small number of >> mutable shared objects, only one can use the channel at once. Since the >> chances are equal whether one or the other gets the channel at any given >> time, but it is impossible that two can be running the accessor code at the >> same time. >> >> Thus, this construction is a mutex because it prevents more than one thread >> accessing at a time. It makes sense to me since it takes several >> instructions to read and write variables copying to register or memory. If >> you put two slots in the buffer, it can run in parallel, that's the point, a >> single element in the channel means only one access at a time and thus it is >> a bottleneck that protects from simultaneous read and right by parallel >> threads. >> >> On Sunday, 17 March 2019 14:55:58 UTC+1, Jan Mercl wrote: >>> >>> On Sun, Mar 17, 2019 at 1:04 PM Louki Sumirniy <louki.sumir...@gmail.com> >>> wrote: >>> >>> > My understanding of channels is they basically create exclusion by >>> > control of the path of execution, instead of using callbacks, or they >>> > bottleneck via the cpu thread which is the reader and writer of this >>> > shared data anyway. >>> >>> The language specification never mentions CPU threads. Reasoning about the >>> language semantics in terms of CPU threads is not applicable. >>> >>> Threads are mentioned twice in the Memory Model document. In both cases I >>> think it's a mistake and we should s/threads/goroutines/ without loss of >>> correctness. >>> >>> Channel communication establish happen-before relations (see Memory Model). >>> I see nothing equivalent directly to a critical section in that behavior, >>> at least as far as when observed from outside. It was mentioned before that >>> it's possible to _construct a mutex_ using a channel. I dont think that >>> implies channel _is a mutex_ from the perspective of a program performing >>> channel communication. The particular channel usage pattern just has the >>> same semantics as a mutex. >>> >>> -- >>> >>> -j >> >> -- >> You received this message because you are subscribed to the Google Groups >> "golang-nuts" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to golang-nuts...@googlegroups.com. >> For more options, visit https://groups.google.com/d/optout. > > -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.