【轉】spin lock 和mutex

立志做一个好的程序员發表於2024-03-11

原文: https://zhuanlan.zhihu.com/p/88427657

package main

import (
	"fmt"
	"log"
	"runtime"
	"sync"
	"sync/atomic"
	"time"
)

// Locker is a spinlock implementation.
//
// A Locker must not be copied after first use.
type Locker struct {
	c sync.Mutex // for copy protection compiler warning
	// lock uintptr
}

// Lock locks l.
// If the lock is already in use, the calling goroutine
// blocks until the locker is available.
func (l *Locker) Lock() {
	// loop:
	// if !atomic.CompareAndSwapUintptr(&l.lock, 0, 1) {
	l.c.Lock()
	// runtime.Gosched()
	// // goto loop
	// l.Unlock()

}

// Unlock unlocks l.
func (l *Locker) Unlock() {
	// atomic.StoreUintptr(&l.lock, 0)
	l.c.Unlock()
}

type SpinLock struct {
	lock uint32
}

// Lock locks the SpinLock.
func (sl *SpinLock) Lock() {
	for !sl.TryLock() {
		runtime.Gosched()
	}
}

// TryLock tries to lock the SpinLock.
func (sl *SpinLock) TryLock() bool {
	return atomic.CompareAndSwapUint32(&sl.lock, 0, 1)
}

// Unlock unlocks the SpinLock.
func (sl *SpinLock) Unlock() {
	atomic.StoreUint32(&sl.lock, 0)
}

func main() {
	runtime.GOMAXPROCS(4)
	// var a []int
	// a = []int{
	// 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 12, 47, 48, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 49, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 73, 100, 101, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 99, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 148, 145, 146, 150, 126, 155, 157, 151, 152, 153, 154, 149, 156, 147, 158, 159, 160, 162, 161, 166, 168, 171, 163, 167, 164, 169, 173, 170, 165, 172, 175, 177, 179, 174, 178, 176, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 180, 203, 205, 206, 207, 208, 209, 210, 204, 211, 213, 214, 215, 216, 217, 224, 226, 219, 220, 221, 222, 223, 212, 225, 218, 228, 229, 227, 231, 232, 233, 234, 235, 236, 237, 238, 239, 102, 240, 241, 247, 248, 242, 250, 244, 251, 253, 243, 249, 245, 257, 260, 230, 254, 262, 263, 246, 258, 264, 274, 276, 279, 256, 259, 281, 284, 267, 268, 269, 270, 271, 272, 273, 252, 275, 261, 277, 278, 255, 280, 285, 282, 287, 266, 265, 283, 288, 289, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 290, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 286, 366, 330, 331, 332, 333, 334, 335, 336, 369, 338, 339, 340, 341, 342, 343, 344, 345, 383, 347, 348, 349, 350, 351, 352, 312, 353, 355, 354, 356, 357, 389, 397, 360, 361, 362, 363, 364, 365, 329, 367, 337, 370, 371, 372, 373, 374, 375, 368, 376, 377, 378, 379, 401, 407, 382, 346, 384, 385, 386, 387, 388, 358, 391, 392, 393, 394, 395, 396, 359, 398, 399, 400, 380, 402, 403, 404, 405, 406, 381, 408, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 409, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 436, 444, 424, 450, 438, 440, 441, 451, 470, 439, 474, 475, 477, 480, 449, 448, 481, 485, 487, 490, 455, 456, 457, 458, 459, 460, 461, 496, 506, 507, 510, 466, 467, 468, 469, 514, 443, 472, 524, 527, 529, 476, 447, 478, 520, 537, 435, 482, 483, 484, 452, 486, 442, 488, 489, 454, 491, 492, 390, 493, 494, 495, 462, 497, 498, 499, 500, 501, 502, 503, 504, 505, 463, 464, 508, 509, 465, 511, 512, 513, 453, 515, 516, 517, 518, 473, 546, 554, 555, 562, 564, 568, 574, 591, 528, 446, 530, 531, 479, 533, 534, 535, 599, 532, 600, 603, 605, 541, 608, 616, 618, 620, 547, 548, 549, 550, 623, 551, 553, 626, 629, 556, 627, 640, 641, 643, 561, 646, 647, 648, 565, 651, 652, 656, 655, 659, 571, 572, 664, 526, 669, 576, 577, 578, 579, 674, 677, 582, 583, 584, 542, 585, 587, 683, 685, 590, 690, 592, 700, 594, 595, 596, 707, 708, 536, 711, 601, 718, 539, 604, 538, 704, 606, 540, 609, 610, 543, 612, 613, 614, 615, 733, 737, 739, 619, 742, 621, 622, 586, 552, 625, 747, 750, 751, 754, 630, 557, 631, 632, 758, 761, 635, 637, 636, 764, 766, 559, 642, 768, 771, 645, 523, 644, 471, 649, 650, 776, 779, 567, 653, 781, 654, 783, 786, 570, 788, 660, 790, 793, 796, 665, 666, 667, 668, 575, 801, 804, 607, 672, 580, 675, 676, 581, 678, 671, 679, 807, 819, 588, 820, 825, 826, 829, 688, 746, 859, 864, 692, 867, 868, 695, 870, 873, 698, 699, 593, 701, 702, 875, 877, 705, 706, 597, 598, 879, 885, 445, 889, 890, 714, 892, 716, 900, 602, 719, 720, 721, 907, 908, 611, 911, 917, 727, 728, 921, 724, 731, 927, 929, 544, 735, 736, 617, 738, 545, 933, 938, 939, 942, 744, 673, 745, 624, 748, 749, 521, 628, 752, 753, 522, 755, 756, 757, 634, 759, 950, 958, 953, 730, 763, 968, 974, 558, 767, 639, 984, 770, 990, 772, 773, 775, 563, 777, 778, 566, 780, 525, 782, 569, 784, 785, 658, 787, 657, 789, 661, 995, 792, 662, 794, 795, 573, 797, 798, 799, 878, 670, 802, 803, 663, 805, 774, 680, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 806, 818, 681, 684, 822, 823, 824, 589, 686, 827, 828, 687, 830, 689, 832, 833, 834, 835, 831, 836, 838, 839, 840, 841, 842, 843, 837, 844, 845, 821, 846, 848, 849, 850, 847, 851, 854, 852, 853, 855, 855, 856, 857, 858, 860, 859, 861, 437, 691, 865, 862, 693, 869, 696, 871, 872, 697, 874, 682, 876, 694, 703, 866, 709, 880, 881, 882, 883, 884, 710, 886, 887, 888, 712, 713, 891, 715, 893, 894, 895, 896, 897, 863, 898, 717, 901, 902, 903, 904, 905, 906, 722, 723, 909, 910, 725, 912, 913, 914, 915, 916, 726, 918, 919, 920, 729, 899, 922, 924, 925, 926, 732, 928, 734, 930, 931, 932, 740, 934, 935, 936, 937, 741, 519, 940, 941, 743, 923, 943, 945, 946, 947, 948, 944, 949, 951, 952, 760, 954, 955, 956, 957, 633, 959, 762, 961, 962, 963, 964, 960, 965, 967, 638, 969, 966, 970, 972, 973, 765, 975, 976, 977, 978, 979, 980, 981, 982, 983, 769, 985, 986, 987, 988, 971, 989, 560, 991, 992, 994, 791, 996, 997, 998, 800, 993,
	// }
	// aDup := make(map[int]bool)
	// for _, v := range a {

	// 	if _, ok := aDup[v]; ok {
	// 		log.Println("exist", v)
	// 		// return
	// 	} else {
	// 		aDup[v] = true
	// 	}
	// }
	t1 := time.Now()
	var wg sync.WaitGroup
	spinlock := Locker{
		c: sync.Mutex{},
	}
	// spinlock := SpinLock{}
	var a int
	for i := 0; i < 3000000; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			spinlock.Lock()
			defer spinlock.Unlock()
			a++
			// 在這裡執行需要互斥訪問的操作
			fmt.Printf("%d\n", a)
		}(i)
	}

	wg.Wait()
	log.Println(time.Since(t1))
}

  

--------------------------------------------------------

spin lock 和mutex

22 人贊同了該文章

一、什麼是spinlock

spinlock又稱自旋鎖,是實現保護共享資源而提出一種鎖機制。自旋鎖與互斥鎖比較類似,都是為了解決對某項資源的互斥使用

無論是互斥鎖,還是自旋鎖,在任何時刻,最多隻能有一個保持者,只能有一個執行單元獲得鎖。但是兩者在排程機制上略有不同。對於互斥鎖,如果資源已經被佔用,資源申請者只能進入睡眠狀態。但是自旋鎖不會引起呼叫者睡眠,如果自旋鎖已經被別的執行單元保持,呼叫者就一直迴圈在那裡看是否該自旋鎖的保持者已經釋放了鎖,"自旋"一詞就是因此而得名

二、spinlock的原理

跟互斥鎖一樣,一個執行單元要想訪問被自旋鎖保護的共享資源,必須先得到鎖,在訪問完共享資源後,必須釋放鎖。如果在獲取自旋鎖時,沒有任何執行單元保持該鎖,那麼將立即得到鎖;如果在獲取自旋鎖時鎖已經有保持者,那麼獲取鎖操作將自旋在那裡,直到該自旋鎖的保持者釋放了鎖。自旋鎖是一種比較低階的保護資料結構或程式碼片段的原始方式,這種鎖可能存在兩個問題:死鎖和過多佔用cpu資源

  • a. 在使用者態嘗試競爭一個共享資源. 如果競爭不到, 則不斷嘗試競爭. 但是不借助核心提供的mutex等變數機制. 因為涉及到核心,就意味這效率低下
  • b. 要想在使用者態實現競爭一個共享資源, 必須藉助cpu提供的原子操作指令. 如果是SMP多cpu,還需要lock指令鎖匯流排
  • c. 為了避免在長時間競爭卻一直得不到資源導致的不斷嘗試浪費cpu, 在每兩次嘗試之間間隔一段時間. 並且隨著嘗試次數的增加,間隔時間也增加.間隔期間可以讓cpu稍加休息(注意,絕不是讓出cpu),這依賴於cpu提供pausse指令. (當然如果cpu沒有提供pause也沒關係,只是會很消耗電力資源)PAUSE指令提升了自旋等待迴圈(spin-wait loop)的效能
  • d. 在等待相當長時間還是得不到鎖之後,只好讓出cpu. 但必須讓出很小一會. 否則就不叫自旋鎖了

如何讓出cpu,卻有可以很快的回來? 核心提供了 sched_yield()函式,sched_yield()主要功能: 簡單的講,可以使用另一個級別等於或高於當前執行緒的執行緒先執行。如果沒有符合條件的執行緒,那麼這個函式將會立刻返回然後繼續執行當前執行緒的程式,如果系統不支援sched_yield, nginx被迫使用了usleep()休息1u秒.

三、spinlock的適用情況

自旋鎖比較適用於鎖使用者保持鎖時間比較短的情況。正是由於自旋鎖使用者一般保持鎖時間非常短,因此選擇自旋而不是睡眠是非常必要的,自旋鎖的效率遠高於互斥鎖

訊號量和讀寫訊號量適合於保持時間較長的情況,它們會導致呼叫者睡眠,因此只能在程序上下文使用,而自旋鎖適合於保持時間非常短的情況,它可以在任何上下文使用。如果被保護的共享資源只在程序上下文訪問,使用訊號量保護該共享資源非常合適,如果對共享資源的訪問時間非常短,自旋鎖也可以。但是如果被保護的共享資源需要在中斷上下文訪問(包括底半部即中斷處理控制代碼和頂半部即軟中斷),就必須使用自旋鎖。自旋鎖保持期間是搶佔失效的,而訊號量和讀寫訊號量保持期間是可以被搶佔的。自旋鎖只有在核心可搶佔或SMP(多處理器)的情況下才真正需要,在單CPU且不可搶佔的核心下,自旋鎖的所有操作都是空操作。另外格外注意一點:自旋鎖不能遞迴使用。

四、spinlock與mutex對比

spinlock不會使執行緒狀態發生切換,mutex在獲取不到鎖的時候會選擇sleep

mutex獲取鎖分為兩階段,第一階段在使用者態採用spinlock鎖匯流排的方式獲取一次鎖,如果成功立即返回;否則進入第二階段,呼叫系統的futex鎖去sleep,當鎖可用後被喚醒,繼續競爭鎖。

Spinlock優點:沒有昂貴的系統呼叫,一直處於使用者態,執行速度快

Spinlock缺點:一直佔用cpu,而且在執行過程中還會鎖bus匯流排,鎖匯流排時其他處理器不能使用匯流排

Mutex優點:不會忙等,得不到鎖會sleep

Mutex缺點:sleep時會陷入到核心態,需要昂貴的系統呼叫

文章來自

相關文章