6 Replies
      Latest reply on Jul 23, 2018 2:31 PM by MuniekMg
      MuniekMg Level 1 Level 1 (0 points)

        Hi,
        Why this piece of code works?

        let queue = DispatchQueue(label: "queue_label", attributes: .concurrent)
        queue.sync {
            queue.sync {
                print("wokrs!")
            }
        }


        shouldn't be a deadlock here?

        Or maybe "attributes: .concurrent" makes .sync and .async always be an "async"?
        Thanks!

        • Re: .sync in concurrent queue
          Ken Thomases Level 4 Level 4 (635 points)

          Synchronous vs. asynchronous and concurrent vs. serial are two separate concepts.  Synchronous vs. asynchronous is about when the caller can continue.  Concurrent vs. serial is about when the dispatched task can run.

           

          The outer queue.sync() call will not return until the closure it is submitting has run to completion.  The closure might be run right on that same thread or it might run on some worker thread maintained by GCD.

           

          The inner queue.sync() call, again, will not return until the closure it is submitting has run to completion.  However, since the queue is concurrent, there's nothing preventing the inner closure from running just because the outer closure is also running.  Concurrent queues allow multiple tasks to run at the same time.

           

          Concurrency is not the same as asynchronicity.  Each queue.sync() call still doesn't return until the submitted closure has completed.

           

          If the queue were serial, then, yes, your code would cause a deadlock.

            • Re: .sync in concurrent queue
              MuniekMg Level 1 Level 1 (0 points)

              Thanks Ken, I think you opened my eyes

              In documentation to dispatch_sync there is this sentecne:
              "As an optimization, this function invokes the block on the current thread when possible."
              So, if it is serial queue then it can run on current thread and also on other thread but concurrent only on other thread? And sync and async has nothing in common with that?

               

              Whether this two pieces of code will behave identically, or there is something more?

              let queue = DispatchQueue(label: "queue_label") // serial queue
              for i in 0..<10 {
                  queue.sync { // sync
                      print(i)
                  }
              }

               

              let queue = DispatchQueue(label: "queue_label", attributes: .concurrent) // concurrent queue
              for i in 0..<10 {
                  queue.sync(flags: .barrier) { // sync with barrier
                      print(i)
                  }
              }


              Thank you!

                • Re: .sync in concurrent queue
                  Ken Thomases Level 4 Level 4 (635 points)

                  You're welcome.

                   

                  Regarding that sentence from the dispatch_sync() documentation: first, whether it runs on the current thread or another thread should not be important to you.  Second, it might run it on the current thread whether the queue is serial or concurrent.  For a serial queue, the task maybe can't run immediately if the queue is already running some other task.  So the current thread will be put to sleep.  When the queue is ready to run your task, it could run it on one of its worker threads that is currently scheduled on the CPU and then wake your thread when it's done, or it could wake your thread and have it run the task.  For a concurrent queue, it's much more likely to run it on the current thread because that thread is ready and already scheduled on the CPU and there's no reason for it to wait (because the queue is concurrent).

                   

                  The task will never run on the current thread if you use an async method.  That's the whole point of async dispatch: it allows the calling thread to go on to other work while the task executes.

                   

                  As to your other question: yes, those two pieces of code will behave identically.  If you always specify .barrier, you've essentially turned your concurrent queue into a serial queue.  I would guess that the concurrent queue code is slightly less efficient than the serial queue code.

                    • Re: .sync in concurrent queue
                      MuniekMg Level 1 Level 1 (0 points)

                      "The task will never run on the current thread if you use an async method.  That's the whole point of async dispatch: it allows the calling thread to go on to other work while the task executes."
                      Of course! my bad!

                      Mean while I edited my question but I will put changes here:

                      1. Why the is a dead lock?

                       

                      let queue = DispatchQueue(label: "queue_label")
                      queue.sync {
                          queue.sync {
                              // Dead lock
                          }
                      }

                       

                      I read that when there is a serial queue and two nested sync exceutes like in example above, first sync() will execute normally, byt second sync() will stop current thread (because it needs to wait for completion) which is the same as thread of a second sync(), so second sync() can never complete - dead lock
                      But now this has no sense for me, because serial queue can execute second sync() on other thread, right?



                        • Re: .sync in concurrent queue
                          Ken Thomases Level 4 Level 4 (635 points)

                          With a serial queue, it will never execute a second task while there's already one executing.  That's the definition of "serial".  The threads don't matter.

                           

                          The first task is what calls the inner queue.sync().  So, the first task is still executing at that point and the queue can't start another task until it completes.  However, because the inner queue.sync() is synchronous, the first task can't complete until the second task (the one it is submitting) completes.  But the second task can't even start, let alone complete, until the first task completes and frees up the queue to run something else.

                           

                          By the way, if you change the outer queue.sync() to queue.async(), you still cause a deadlock.  Your thread can continue, but whatever worker thread runs the outer task will deadlock and that serial queue will never run anything else.  The important thing is dispatching a task synchronously to a serial queue from a task which is running on that same serial queue.

                            • Re: .sync in concurrent queue
                              MuniekMg Level 1 Level 1 (0 points)

                              "ith a serial queue, it will never execute a second task while there's already one executing.  That's the definition of "serial".  The threads don't matter."
                              Yes, I was wondering about that and just figured it out and wanted to write it here, but again, you were faster!

                              Thank you very much for all informations! It really helped me to understand the differences!

                              Best regards!