Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to find out when to lock a shared variable or not.

For scalars, it seems obvious: only when you want to modify it. It seems that there is no problem with concurrent read access to the same scalar, right?

Then, if you have one write and multiple reads to a single scalar, it seems you do not need locking either. Each read either gets the value before the write, or the one after, but not something else. Correct?

So, it seems you only need locking when you may have multiple concurrent write operations.

But what about hashes? Is it also true?

Are all the read operations below safe without locking?

my %h : shared; $x = $h{foo}; # R1 safe? @x = keys(%h); # R2 safe? while (($k, $v) = each(%h)) { ... } # R3 safe?

Then, is it ok, like for scalars, to have multiple reads and only one write without locking? Here are the write operations I have in mind:

$h{foo} = "change the existing 'foo' entry"; # W1 aka modification $h{bar} = "add a 'bar' entry"; # W2 aka insertion delete($h{zoi}); # W3 aka deletion

For R1, I hope that locking is not needed: you either get the value before the modification/insertion/deletion or the one after. Could you please confirm?.

Will R2 work while the hash is being modified? Of course, by the time you use the keys, the hash could have changed but if you simply access the hash with R1, there should be no problem: you may miss some keys or access non existing entries.

For R3, will it work at all?

Thanks in advance for your wisdom...

Replies are listed 'Best First'.
Re: threads::shared - when to lock a hash ?
by zwon (Abbot) on Oct 16, 2011 at 07:45 UTC
    Then, if you have one write and multiple reads to a single scalar, it seems you do not need locking either. Each read either gets the value before the write, or the one after, but not something else. Correct?

    No, not correct, you can get inconsistent value in the reading thread. You always have to lock shared variable when you accessing it.

      You always have to lock shared variable when you accessing it
      Wrong. Perl internally locks the variable before reading from or writing to it, so you don't need to lock it to avoid a corrupted value.

      As to the OP's questions: R1 is safe, while R2 behaves like R3: i.e. it iterates over the hash pushing individual values onto @x. In R3, if another thread is adding or deleting elements, or also iterating, then it will affect the result in the same way as if a single thread was doing it, e.g.

      while (($k, $v) = each(%h)) { # stuff here which adds or deletes elements, # or uses each/keys/values }
      W1,2,3 are all safe.

      Of course in all the above, "safe" means "not corrupting perl's internal state"; perl's internal locking causes all the reads and writes to be serialised. You may of course still need to do locking at a higher level to ensure the safety of your own code's logic

      Dave.

        Perl internally locks the variable before reading from or writing to it

        Thanks, I should have thought that perl somehow protects its internal state. I couldn't find anything in threads::shared manual, so I looked into source and what I understand is that perl locks not just variable but the whole shared space, is it correct?

        so you don't need to lock it to avoid a corrupted value.

        It's possible that the ops return junk if all the lock does is avoid corruption. I'm not sure this answers anything.

        For starters, does a hash have one iterator per thread or just one iterator? each and possibly keys aren't safe if it's the latter.

Re: threads::shared - when to lock a hash ?
by zentara (Cardinal) on Oct 16, 2011 at 13:49 UTC

      Who do you think?

        I tend to believe dave_the_m because I'ver never used locking in any of my threaded scripts, and as of yet, never seen a problem. dave_the_m even commented on some old script of mine, without locking used, and mused hmmmm,.. some sort of low-level atomic locking seems to be occurring automatically

        Just like a Prime Directive :-)

        But I will quote his fine print:

        Of course in all the above, "safe" means "not corrupting perl's internal state"; perl's internal locking causes all the reads and writes to be serialised. You may of course still need to do locking at a higher level to ensure the safety of your own code's logic


        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh
Re: threads::shared - when to lock a hash ?
by locked_user sundialsvc4 (Abbot) on Oct 16, 2011 at 13:33 UTC

    A shared resource, whether a variable or not, must be locked “anytime.”   It is not merely to ensure that the value is not physically destroyed.   There are two use-cases that must be guarded; they are separate but related.

    1. Read stability:   This is the “write once, read many” locking scenario.   Many processes, all of whom are “merely reading” a shared resource, need to block anyone else from making a modification at the same time, although they don’t care how many others may be reading.
    2. Write stability:   The more-obvious requirement.   Processes that are updating a resource need to avoid race conditions that could cause the value to be destroyed ... whether in the literal sense of data-structure destruction or simply in the sense of unpredictable (and therefore, useless) final values.   This lock is exclusive and cannot be obtained until all other write and read locks are satisfied.

    The first requirement, not merely the second one, dictates the true and comprehensive need for locking.   It applies not only in the Perl environment but also in every other situation.

    Effective locking can be voluntary, e.g. the SELECT LOCK(name) capability of MySQL.   If there is known to be complete and full-coverage cooperation among all possible accessors of a resource, process synchronization can be achieved by placing locks upon a pool of arbitrarily-named strings, provided that the protocol for constructing those strings is carefully and completely known by all.

      The first requirement ... dictates the true and comprehensive need for locking. It applies not only in the Perl environment but also in every other situation.

      You're at it again. Spouting utter twaddle! Even in the face of prior authoritative information in this thread by the man that knows.

      For any value that can be read & written atomically -- which, for example in C on a 64-bit processor, includes all suitably aligned integers, floating point values and addresses up to 64-bits in length -- locking is unnecessary for reading or writing.

      And in the case of Perl and threads::shared, scalar variables -- including those embedded within hashes or arrays -- can be read at any time without locking. Even if another thread is attempting to modify that same scalar. You will either get the value before the writer writes, or you'll get the value after it has written.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

      Okay, I am not even going to attempt to engage BrowserUK in his readily-expected and intensely personal thread ... a response which I have sadly grown to expect but choose not to answer in kind.

      The statement is, of course, entirely correct that concurrent access to a data structure in Perl by multiple threads will not cause the self-destruction of memory integrity as, say, the alteration of the guts of a "C" linked-list data structure might do.   The Implementors™ certainly got that much (my point #2 above) right.   But ...

      My point #1 is a fundamental-design issue.   Cooperating processes/threads who are not modifying a data structure, and who never intend to modify it, nonetheless need to be safeguarded against anyone else who from time to time may wish to do so.   Otherwise, and even though the Perl (or any other...) data structures remain perfectly “intact,” they might find without warning that “reality has changed;” that the proverbial rug has just been pulled out from under them.   The concurrent readers don’t need to block one-another, but all of them do need to block (and to be blocked by) all attempted modifications.   In this way, they will properly perceive all of the groups of writes that from time-to-time do occur as being “atomic,” whether those updates consist of a single variable-update or many.

      I will discuss the technical issue only to the extent necessary to clearly convey it.   I will not discuss “personalities” here, even if overtly provoked, and I would kindly ask that (the remainder of) the Community drop the subject and refuse ever to pick it up again.   We are here only to discuss the technical concerns that professionally face us all; not each another.

        The above reply, of course, was mine.   PerlMonks timed out on me.   I think that nothing more need be said ... or, should be said.   Please.   And, if anything else “personal” is said, I shall never reply to it.