This is the 17th day of my participation in the August Text Challenge.More challenges in August

A deadlock

Four conditions for deadlocks

  • Can’t be deprived

Resources acquired by a thread cannot be taken away by other threads before they are used up, but can only be released after they are used up.

  • The request to keep

Thread T1 holds one resource, R1, but makes another request for resource, R2, which is occupied by thread T2. Therefore, thread T1 must wait, but does not release its held R1 resource.

  • Loop waiting for

When a deadlock occurs, there must be a process-resource ring chain. For example, process P0 waits for P1 to occupy resources, and process P1 waits for resources occupied by p2, and process P2 waits for resources occupied by P0.

  • The mutex

Threads are exclusive to resource access, and if one thread occupies a resource, the other threads must wait until the resource is released.

How do I avoid deadlocks

  • If multiple tables are queried concurrently, specify the access order

It is not possible for thread T1 to access table A and then B, and thread T2 to access table B and then A, which is extremely vulnerable to deadlocks.

  • Lock as many resources as you need at once in the same transaction as possible

  • For deadlock-prone business scenarios, try to upgrade the locking force

  • Use distributed locks or optimistic locks

Deadlock code

package sync

import (
   "fmt"
   "runtime"
   "sync"
   "testing"
   "time"
)
type value struct {
   memAccess sync.Mutex
   value     int
}
func TestDeadLock(t *testing.T) {
   runtime.GOMAXPROCS(3)
   var wg sync.WaitGroup
   sum := func(v1, v2 *value) {
      defer wg.Done()
      v1.memAccess.Lock()  / / lock v1
      time.Sleep(2 * time.Second)
      v2.memAccess.Lock() / / lock v2
      fmt.Printf("sum = %d\n", v1.value+v2.value)
      v2.memAccess.Unlock()
      v1.memAccess.Unlock()
   }
   product := func(v1, v2 *value) {
      defer wg.Done()
      v2.memAccess.Lock() / / lock v2
      time.Sleep(2 * time.Second)
      v1.memAccess.Lock() / / lock v1
      fmt.Printf("product = %d\n", v1.value*v2.value)
      v1.memAccess.Unlock()
      v2.memAccess.Unlock()
   }
   var v1, v2 value
   v1.value = 1
   v2.value = 1
   wg.Add(2)
   go sum(&v1, &v2)
   go product(&v1, &v2)
   wg.Wait()
}
Copy the code

The results

=== RUN   TestDeadLock
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
testing.(*T).Run(0xc000122480, 0x116dd2c, 0xc, 0x1176e68, 0x1084de6)
	/usr/local/go/src/testing/testing.go:1240 +0x2da
testing.runTests.func1(0xc000122300)
	/usr/local/go/src/testing/testing.go:1512 +0x78
testing.tRunner(0xc000122300, 0xc00012dde0)
	/usr/local/go/src/testing/testing.go:1194 +0xef
testing.runTests(0xc0001320d8, 0x12540e0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x116e218)
	/usr/local/go/src/testing/testing.go:1510 +0x2fe
testing.(*M).Run(0xc00014c080, 0x0)
	/usr/local/go/src/testing/testing.go:1418 +0x1eb
main.main()
	_testmain.go:51 +0x138
Copy the code

Fatal error: All goroutines are ASLEEP – deadlock! Thread T1 gets v1, then gets v2, thread T2 gets v2, then gets v1. This satisfies the conditions such as deadlock cycle waiting, which causes deadlock.