Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Read-Write thread synchronization

Read-Write thread synchronization

Scheduled Pinned Locked Moved C#
helpquestionlearning
7 Posts 2 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    Den2Fly
    wrote on last edited by
    #1

    Hello everybuddy, I am a little confused about why reading and writing a resource concurrently may be unsafe in some scenarios as I explain: 1. Blocking multiple threads from writing a resource based on its current value (such as incrementing some number, or manipulating some collections) is logical and I have no problem with it. 2. Blocking multiple threads from setting a resource to some new value independent of its current value seems unnecessary in my opinion, I need some clarification by some friend here why such synchronization is required. (more details: while there is no guarantee that if we block at start of the write process, which thread enters the section first, then that should not be important in not-locking scenario, which thread completes actual write instructions first and the other overwrites it, so why should we lock?! ). 3. Blocking two threads that one is writing the resource and another is reading it, also seems unnecessary, again I need clarification. (More details: Again we don't know which thread enters the locked section first, so that should not be important in not-locking scenario if the reader thread reads the value before the write process completely takes place or after that). Thank you so much for any help - den2fly

    --- "Art happens when you least expect it."

    L 1 Reply Last reply
    0
    • D Den2Fly

      Hello everybuddy, I am a little confused about why reading and writing a resource concurrently may be unsafe in some scenarios as I explain: 1. Blocking multiple threads from writing a resource based on its current value (such as incrementing some number, or manipulating some collections) is logical and I have no problem with it. 2. Blocking multiple threads from setting a resource to some new value independent of its current value seems unnecessary in my opinion, I need some clarification by some friend here why such synchronization is required. (more details: while there is no guarantee that if we block at start of the write process, which thread enters the section first, then that should not be important in not-locking scenario, which thread completes actual write instructions first and the other overwrites it, so why should we lock?! ). 3. Blocking two threads that one is writing the resource and another is reading it, also seems unnecessary, again I need clarification. (More details: Again we don't know which thread enters the locked section first, so that should not be important in not-locking scenario if the reader thread reads the value before the write process completely takes place or after that). Thank you so much for any help - den2fly

      --- "Art happens when you least expect it."

      L Offline
      L Offline
      Luc Pattyn
      wrote on last edited by
      #2

      For sure, there are situations where a single variable can be written and read by multiple threads without needing a lock. And there are some situations where a more complex set of data can be maintained without a lock. Example: a circular buffer used by only one producer and only one consumer. When you assemble such queue with a fixed-size array, a last-produced index and a last-consumed index, it can work perfectly without any synchronization. But then there are many situations where a group of values describe the state of something, and not all combinations of values are allowed; so when a new state needs to be established multiple values need updating, and reading some of them while that update is happening would result in an unconsistent view. Here you definitely need locks. Hope this helps. :)

      Luc Pattyn

      D 1 Reply Last reply
      0
      • L Luc Pattyn

        For sure, there are situations where a single variable can be written and read by multiple threads without needing a lock. And there are some situations where a more complex set of data can be maintained without a lock. Example: a circular buffer used by only one producer and only one consumer. When you assemble such queue with a fixed-size array, a last-produced index and a last-consumed index, it can work perfectly without any synchronization. But then there are many situations where a group of values describe the state of something, and not all combinations of values are allowed; so when a new state needs to be established multiple values need updating, and reading some of them while that update is happening would result in an unconsistent view. Here you definitely need locks. Hope this helps. :)

        Luc Pattyn

        D Offline
        D Offline
        Den2Fly
        wrote on last edited by
        #3

        Thank you very much for the helpful info yo provided. But I still need dedicated replies on the three situations I explained in details.

        --- "Art happens when you least expect it."

        L 1 Reply Last reply
        0
        • D Den2Fly

          Thank you very much for the helpful info yo provided. But I still need dedicated replies on the three situations I explained in details.

          --- "Art happens when you least expect it."

          L Offline
          L Offline
          Luc Pattyn
          wrote on last edited by
          #4

          If you provide a code snippet where you are not sure a lock is required, I could help you in showing it does or does not need a lock. For each method shown, please indicate how many threads (1 or more?) could be executing it. :)

          Luc Pattyn

          D 1 Reply Last reply
          0
          • L Luc Pattyn

            If you provide a code snippet where you are not sure a lock is required, I could help you in showing it does or does not need a lock. For each method shown, please indicate how many threads (1 or more?) could be executing it. :)

            Luc Pattyn

            D Offline
            D Offline
            Den2Fly
            wrote on last edited by
            #5

            Sample: Here I have a collection class that I want to be thread safe for reading from and manipulation (Not also in the case of iterations). It is assumed any number of threads may enter any of the methods or indexers at the same time. I think for reading from indexers there is no need for a lock, even if there is a chance for more than one thread to enter the indexers, and/or at the same time to one of the Add, Remove, Clear methods. Is it correct? (sorry I don't know how to format code snippets in the post) internal class MyThreadSafeCollection : System.Collections.Specialized.NameObjectCollectionBase { const int readLockTimeout = 100; const int writeLockTImeout = 100; private ReaderWriterLock rwLock = new ReaderWriterLock(); public MyThreadSafeCollection() { } public object this[int index] { get { rwLock.AcquireReaderLock(readLockTimeout); // IS THIS REQUIRED ?? try { return BaseGet(index); } finally { rwLock.ReleaseReaderLock(); } } } public SocketClientData this[string id] { get { rwLock.AcquireReaderLock(readLockTimeout); // IS THIS REQUIRED ?? try { return BaseGet(id); } finally { rwLock.ReleaseReaderLock(); } } } public void Add(string id, SocketClientData client) { rwLock.AcquireWriterLock(writeLockTImeout); // I Know it is required. (isn't it?) try { BaseAdd(id, client); } finally { rwLock.ReleaseWriterLock(); } } public void Remove(string id) { rwLock.AcquireWriterLock(writeLockTImeout); // I Know it is required. (isn't it?) try { BaseRemove(id); } finally { rwLock.ReleaseWriterLock(); } } public void Clear() { rwLock.AcquireWriterLock(writeLockTImeout); // I Know it is required. (isn't it?) try { BaseClear(); } finally { rwLock.ReleaseWriterLock(); } }

            L 1 Reply Last reply
            0
            • D Den2Fly

              Sample: Here I have a collection class that I want to be thread safe for reading from and manipulation (Not also in the case of iterations). It is assumed any number of threads may enter any of the methods or indexers at the same time. I think for reading from indexers there is no need for a lock, even if there is a chance for more than one thread to enter the indexers, and/or at the same time to one of the Add, Remove, Clear methods. Is it correct? (sorry I don't know how to format code snippets in the post) internal class MyThreadSafeCollection : System.Collections.Specialized.NameObjectCollectionBase { const int readLockTimeout = 100; const int writeLockTImeout = 100; private ReaderWriterLock rwLock = new ReaderWriterLock(); public MyThreadSafeCollection() { } public object this[int index] { get { rwLock.AcquireReaderLock(readLockTimeout); // IS THIS REQUIRED ?? try { return BaseGet(index); } finally { rwLock.ReleaseReaderLock(); } } } public SocketClientData this[string id] { get { rwLock.AcquireReaderLock(readLockTimeout); // IS THIS REQUIRED ?? try { return BaseGet(id); } finally { rwLock.ReleaseReaderLock(); } } } public void Add(string id, SocketClientData client) { rwLock.AcquireWriterLock(writeLockTImeout); // I Know it is required. (isn't it?) try { BaseAdd(id, client); } finally { rwLock.ReleaseWriterLock(); } } public void Remove(string id) { rwLock.AcquireWriterLock(writeLockTImeout); // I Know it is required. (isn't it?) try { BaseRemove(id); } finally { rwLock.ReleaseWriterLock(); } } public void Clear() { rwLock.AcquireWriterLock(writeLockTImeout); // I Know it is required. (isn't it?) try { BaseClear(); } finally { rwLock.ReleaseWriterLock(); } }

              L Offline
              L Offline
              Luc Pattyn
              wrote on last edited by
              #6

              OK, AFAIK the general answer is yes, you need all the locks: 1) for operations that modify the data (add, remove, clear) if more than one thread were to perform a modification at the same time, the result would be unpredictable without lock. 2) for operations that just read the data, if another thread were to write at the same time, the results again COULD be unpredictable (it depends on how the data actually is structured), so you should acquire a reader lock (which is less demanding than a writer lock, it does not block other readers on the same object). 3) of course there is an overall concern as to where the lock needs to be implemented. My answer typically is at the highest possible level, i.e. the level where your functional primitives are located. Example: if you use an indexor into a collection, what is the meaning of the index if the collection could change in the mean time ?

              void sumAll(myCollection) {
              int sum=0;
              // would consider a global acquire lock here
              int n=myCollection.Count;
              for (int i=0; i

              in the above snippet, seems to me the entire for loop needs a lock, otherwise how
              can we be sure we get the collection items we intended, since without an overall lock
              the collection (and hence the index-object relations) may change (probably not so
              if lookup by key). In the example, the items summed may not be the ones that were in
              the collection at the time the count was obtained !

              Once you do use an overall lock, you may not want a low-level lock
              anymore (and that is, I guess, why the NET Collections dont have a lock built-in).

              So in conclusion:

              • there has to be a lock on all operations
              • positioning the lock code is a design issue:
                too low ==> may produce wrong results
                too high ==> may be counterproductive (multithreading not working anymore),
                and possibly deadlocks
                as for performance, locks at higher level are better (fewer acq/rel).
              • All this makes it difficult, if not impossible, to build a really useful thread-safe
                collection, independent of the application itself.

              Hope this helps.

              :)

              Luc Pattyn

              D 1 Reply Last reply
              0
              • L Luc Pattyn

                OK, AFAIK the general answer is yes, you need all the locks: 1) for operations that modify the data (add, remove, clear) if more than one thread were to perform a modification at the same time, the result would be unpredictable without lock. 2) for operations that just read the data, if another thread were to write at the same time, the results again COULD be unpredictable (it depends on how the data actually is structured), so you should acquire a reader lock (which is less demanding than a writer lock, it does not block other readers on the same object). 3) of course there is an overall concern as to where the lock needs to be implemented. My answer typically is at the highest possible level, i.e. the level where your functional primitives are located. Example: if you use an indexor into a collection, what is the meaning of the index if the collection could change in the mean time ?

                void sumAll(myCollection) {
                int sum=0;
                // would consider a global acquire lock here
                int n=myCollection.Count;
                for (int i=0; i

                in the above snippet, seems to me the entire for loop needs a lock, otherwise how
                can we be sure we get the collection items we intended, since without an overall lock
                the collection (and hence the index-object relations) may change (probably not so
                if lookup by key). In the example, the items summed may not be the ones that were in
                the collection at the time the count was obtained !

                Once you do use an overall lock, you may not want a low-level lock
                anymore (and that is, I guess, why the NET Collections dont have a lock built-in).

                So in conclusion:

                • there has to be a lock on all operations
                • positioning the lock code is a design issue:
                  too low ==> may produce wrong results
                  too high ==> may be counterproductive (multithreading not working anymore),
                  and possibly deadlocks
                  as for performance, locks at higher level are better (fewer acq/rel).
                • All this makes it difficult, if not impossible, to build a really useful thread-safe
                  collection, independent of the application itself.

                Hope this helps.

                :)

                Luc Pattyn

                D Offline
                D Offline
                Den2Fly
                wrote on last edited by
                #7

                Thank you very very much buddy, Yes that really helped and clarified a lot for me :-) I wish I can help u back some day.

                --- "Art happens when you least expect it."

                1 Reply Last reply
                0
                Reply
                • Reply as topic
                Log in to reply
                • Oldest to Newest
                • Newest to Oldest
                • Most Votes


                • Login

                • Don't have an account? Register

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • World
                • Users
                • Groups