4.1/4.2 多線程進階篇<上>(Pthread & NSThread)

来源:http://www.cnblogs.com/shorfng/archive/2016/03/25/5318557.html
-Advertisement-
Play Games

因為Pthread很少用到,所以對於Pthread的知識沒有摳那麼細緻,所以將Pthread和 NSThread放在了一起。 4.1 Pthread 4.1-1.0 創建線程 - pthread_create 4.1-1.1 __bridge 橋接 <!--?xml version="1.0" en ...


因為Pthread很少用到,所以對於Pthread的知識沒有摳那麼細緻,所以將Pthread和 NSThread放在了一起。

 

4.1 Pthread


4.1-1.0 創建線程 - pthread_create

 1 /*
 2 <#pthread_t *restrict#>  線程的id,指向線程標識符的指針,C 語言中類型的結尾通常 _t/Ref,而且不需要使用 *
 3 <#const pthread_attr_t *restrict#> 用來設置線程的屬性 (一般為 NULL)
 4 <#void *(*)(void *)#>  新建立的線程執行代碼的函數,線程開啟後需要調用的函數或方法 (指向函數的指針)
 5 <#void *restrict#>     運行函數的參數,線程的限制 (一般為 NULL)
 6 */
 7 
 8 返回值:
 9  - 若線程創建成功,則返回0
10  - 若線程創建失敗,則返回出錯編號
11 
12 pthread_create(
13           <#pthread_t *restrict#>,   // pthread_t :線程標識符.
14           <#const pthread_attr_t *restrict#>,
15           <#void *(*)(void *)#>,
16           <#void *restrict#>
17 );

 

4.1-1.1 __bridge 橋接

__bridge 橋接:
1、在c語言和OC之間,對數據類型進行轉成換 2、通過橋接告訴c語言的函數,name就由C語言去管了
橋接的目的 :  就是為了告訴編譯器如何管理記憶體,為OC添加自動記憶體管理操作
  小結 :
  • 在 C 語言中,沒有對象的概念,對象是以結構體的方式來實現的
  • 通常,在 C 語言框架中,對象類型以 _t/Ref 結尾,而且聲明時不需要使用 *
  • C 語言中的 void * 和 OC 中的 id 是等價的
  • 在混合開發時,如果在 C 和 OC 之間傳遞數據,需要使用 __bridge 進行橋接,
  • 橋接的添加可以藉助 Xcode 的輔助功能添加
  number = 1: 表示 主線程 number != 1: 表示 子線程   C語言中 void * == OC中的id C語言的數據類型,一般以  Ref / _t
  • OC框架 Foundation
  •   C語言 Core Foundation
   4.1-1.2【代碼】Pthread
 1 首先導入頭文件
 2 
 3 #import <pthread.h>
 4 
 5 代碼創建:
 6 
 7 // 創建線程,並且線上程中執行 demo 函數
 8 - (void)pthreadDemo {
 9 
10   pthread_t threadId = NULL;
11   NSString *str = @"Hello Pthread";
12 
13   int result = pthread_create(&threadId, NULL, demo, (__bridge void *)(str));
14 
15   if (result == 0) {
16     NSLog(@"創建線程 OK");
17   } else {
18     NSLog(@"創建線程失敗 %d", result);
19   }
20 }
21 
22 // 後臺線程調用函數
23 void *demo(void *params) {
24   NSString *str = (__bridge NSString *)(params);
25 
26   NSLog(@"%@ - %@", [NSThread currentThread], str);
27 
28   return NULL;
29 }

 

 

4.2  NSThread


 

4.2-1.0 創建線程

  • 第一種:通過NSThread的對象方法 (alloc / init - start)
  • 第二種:通過NSThread的類方法    (detachNewThreadSelector)
  • 第三種:通過NSObject的方法
 4.2-1.1 創建線程1 - 對象方法alloc/init 一個NSThread對象就代表一條線程
1 創建方式1 : 通過NSThread的對象方法 (先創建初始化線程alloc/init , 再 start 開啟線程)   ——調試方便
2 
3 NSThread *thread = [[NSThread alloc]initWithTarget:<#(nonnull id)#>
4                                           selector:<#(nonnull SEL)#>
5                                             object:<#(nullable id)#> ];
 1 #import "ViewController.h"
 2 
 3 @interface ViewController ()
 4 @end
 5 
 6 @implementation ViewController
 7 
 8 - (void)viewDidLoad {
 9   [super viewDidLoad];
10   // Do any additional setup after loading the view, typically from a nib.
11 }
12 
13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
14   [self threadDemo1];
15 }
16 
17 #pragma mark - 對象方法alloc/init
18 /*
19  - 在 OC 中,任何一個方法的代碼都是從上向下順序執行的
20  - 同一個方法內的代碼,都是在相同線程執行的(block除外)
21  */
22 - (void)threadDemo1 {
23   NSLog(@"before %@", [NSThread currentThread]);
24 
25   // 線程一啟動,就會線上程thread中執行self的run方法
26   NSThread *thread = [[NSThread alloc] initWithTarget:self
27                                              selector:@selector(longOperation:)
28                                                object:@"THREAD"];
29 
30   //開啟線程,通過start方法,就會將我們創建出來的當前線程加入到`可調度線程池`,供CPU調度
31   //[thread start];執行後,會在另外一個線程執行 longOperation: 方法
32   [thread start];
33 
34   NSLog(@"after %@", [NSThread currentThread]);
35 }
36 
37 - (void)longOperation:(id)obj {
38   NSLog(@"%@ - %@", [NSThread currentThread], obj);
39 }
40 
41 - (void)didReceiveMemoryWarning {
42   [super didReceiveMemoryWarning];
43   // Dispose of any resources that can be recreated.
44 }
45 
46 @end
列印結果:

2016-03-17 18:19:41.878 創建線程1 - 對象方法[2543:387435] before <NSThread: 0x7ffd28c00ca0>{number = 1, name = main}
2016-03-17 18:19:41.880 創建線程1 - 對象方法[2543:387711] <NSThread: 0x7ffd28d77be0>{number = 2, name = (null)} - THREAD
2016-03-17 18:19:41.880 創建線程1 - 對象方法[2543:387435] after <NSThread: 0x7ffd28c00ca0>{number = 1, name = main}

  

4.2-1.1.1 Target

NSThread 的實例化方法中的 target 指的是開啟線程後,線上程中執行 哪一個對象 的 @selector 方法
1 NSThread *thread = [[NSThread alloc] initWithTarget:self.person
2                                            selector:@selector(longOperation:)
3                                              object:@"THREAD"];
4 
5 [thread start];
  • 通過指定不同的 target 會在後臺線程執行該對象的 @selector 方法
  • 不要看見 target 就寫 self

 

4.2-1.2 創建線程2 - 類方法
1 創建方式2 : 通過NSThread的類方法 (創建線程後直接自動啟動線程)
2 
3 [NSThread detachNewThreadSelector:<#(nonnull SEL)#>
4                          toTarget:<#(nonnull id)#>
5                        withObject:<#(nullable id)#> ];
 1 #import "ViewController.h"
 2 
 3 @interface ViewController ()
 4 @end
 5 
 6 @implementation ViewController
 7 
 8 - (void)viewDidLoad {
 9   [super viewDidLoad];
10   // Do any additional setup after loading the view, typically from a nib.
11 }
12 
13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
14   [self threadDemo2];
15 }
16 
17 #pragma mark - 類方法
18 - (void)threadDemo2 {
19   NSLog(@"before %@", [NSThread currentThread]);
20 
21   // detachNewThreadSelector 類方法不需要啟動,會自動創建線程並執行@selector方法
22   // 它會自動給我們做兩件事 :  1.創建線程對象  2.添加到`可調度線程池`
23   // 通過NSThread的類和NSObject的分類方法直接加入到可調度線程池裡面去,等待CPU調度
24   [NSThread detachNewThreadSelector:@selector(longOperation:)
25                            toTarget:self
26                          withObject:@"DETACH"];
27 
28   NSLog(@"after %@", [NSThread currentThread]);
29 }
30 
31 - (void)longOperation:(id)obj {
32   NSLog(@"%@ - %@", [NSThread currentThread], obj);
33 }
34 
35 - (void)didReceiveMemoryWarning {
36   [super didReceiveMemoryWarning];
37   // Dispose of any resources that can be recreated.
38 }
39 
40 @end
列印結果:

2016-03-17 18:36:05.339 創建線程2 - 類方法[2647:404930] before <NSThread: 0x7fddf8f01eb0>{number = 1, name = main}
2016-03-17 18:36:05.340 創建線程2 - 類方法[2647:404930] after <NSThread: 0x7fddf8f01eb0>{number = 1, name = main}
2016-03-17 18:36:05.340 創建 線程2 - 類方法[2647:405061] <NSThread: 0x7fddf8e0e7a0>{number = 2, name = (null)} - DETACH

 

4.2-1.3 創建線程3 - 分類方法(NSObject)

1 創建方式3 : 通過NSObject的分類方法  (隱式創建並直接自動啟動線程)   ——推薦,開發常用
2 
3 // 此方法在後臺線程中執行 (即是 : 在子線程中執行)
4 [self performSelectorInBackground:<#(nonnull SEL) #>
5                        withObject:<#(nullable id) #> per];
 1 #import "ViewController.h"
 2 
 3 @interface ViewController ()
 4 @end
 5 
 6 @implementation ViewController
 7 
 8 - (void)viewDidLoad {
 9   [super viewDidLoad];
10   // Do any additional setup after loading the view, typically from a nib.
11 }
12 
13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
14   [self threadDemo3];
15 }
16 
17 #pragma mark - 分類方法
18 - (void)threadDemo3 {
19   NSLog(@"before %@", [NSThread currentThread]);
20 
21   // performSelectorInBackground 是NSObject的分類方法,會自動在後臺線程執行@selector方法
22   // 沒有 thread 字眼,隱式創建並啟動線程
23   // 所有 NSObject 都可以使用此方法,在其他線程執行方法
24   // 通過NSThread的類和NSObject的分類方法直接加入到可調度線程池裡面去,等待CPU調度
25   // PerformSelectorInBackground 可以讓方便地在後臺線程執行任意NSObject對象的方法
26   [self performSelectorInBackground:@selector(longOperation:)
27                          withObject:@"PERFORM"];
28 
29   NSLog(@"after %@", [NSThread currentThread]);
30 }
31 
32 - (void)longOperation:(id)obj {
33   NSLog(@"%@ - %@", [NSThread currentThread], obj);
34 }
35 
36 - (void)didReceiveMemoryWarning {
37   [super didReceiveMemoryWarning];
38   // Dispose of any resources that can be recreated.
39 }
40 
41 @end
列印結果:

2016-03-17 18:41:58.696 創建線程3 - 分類方法[2711:412522] before <NSThread: 0x7ff078c02320>{number = 1, name = main}
2016-03-17 18:41:58.696 創建線程3 - 分類方法[2711:412522] after <NSThread: 0x7ff078c02320>{number = 1, name = main}
2016-03-17 18:41:58.697 創建線程3 - 分類方法[2711:412751] <NSThread: 0x7ff078c0e390>{number = 2, name = (null)} - PERFORM

  

方式2和方式3的優缺點 :
優點:簡單快捷 缺點:無法對線程進行更詳細的設置

 

4.2-2.0 線程屬性

 1 主線程相關方法 :
 2 
 3 + (NSThread *)mainThread; // 獲得主線程
 4 - (BOOL)isMainThread; // 是否為主線程
 5 + (BOOL)isMainThread; // 是否為主線程
 6 
 7 NSThread *main = [NSThread mainThread];   // + (NSThread *)mainThread;  獲得主線程
 8 
 9 [NSThread  isMainThread]; //  + (BOOL)isMainThread;    類方法判斷,該方法是否為主線程
10 
11 [main isMainThread];      //  - (BOOL)isMainThread;   對象方法判斷,該對象是否為主線程
 1 其他用法:
 2 (1) currentThread - 獲得當前線程 :
 3 
 4 舉例 :
 5 NSThread *current = [NSThread currentThread]; //獲得當前線程
 6 
 7 (2) threadPriority - 線程的調度優先順序 :
 8 
 9 優先順序,是一個浮點數,取值範圍從 0~1.0 預設優先順序是0.5 值越大,優先順序越高
10 
11 優先順序高只是保證 CPU 調度的可能性會高
12 
13 建議:在開發的時候,不要修改優先順序
14 多線程的目的:是將耗時的操作放在後臺,不阻塞主線程和用戶的交互!
15 多線程開發的原則:簡單
16 
17 //返回當前方法所線上程的優先順序
18 + (double)threadPriority;
19 舉例:[NSThread threadPriority];
20 
21 //設置線程的優先順序
22 + (BOOL)setThreadPriority:(double)p;
23 舉例:self.thread1.threadPriority = 1.0;
24 
25 - (double)threadPriority;//返回當前方法所線上程的優先順序
26 - (BOOL)setThreadPriority:(double)p;//設置線程的優先順序
27 
28 (3) name - 線程的名字 : 在大的商業項目中,通常需要在程式崩潰時,獲取程式準確執行所在的線程
29 
30 - (void)setName:(NSString *)n;   //set 方法
31 - (NSString *)name;              //get 方法
32 
33 舉例:
34 thread.name = @"線程A";
35 
36 (4) stackSize - 棧區大小
37 
38 - 預設情況下,無論是主線程還是子線程,棧區大小都是 512K
39 - 棧區大小雖然可以設置,但是我們一般都使用系統預設的大小就行了
40 
41 舉例:
42 [NSThread currentThread].stackSize = 1024 * 1024;

代碼示例:

 1 // 線程屬性
 2 - (void)threadProperty {
 3   NSThread *t1 = [[NSThread alloc] initWithTarget:self
 4                                          selector:@selector(demo)
 5                                            object:nil];
 6 
 7   // 1. 線程名稱
 8   t1.name = @"Thread AAA";
 9 
10   // 2. 優先順序
11   t1.threadPriority = 0;
12 
13   [t1 start];
14 
15   NSThread *t2 = [[NSThread alloc] initWithTarget:self
16                                          selector:@selector(demo)
17                                            object:nil];
18   // 1. 線程名稱
19   t2.name = @"Thread BBB";
20   // 2. 優先順序
21   t2.threadPriority = 1;
22 
23   [t2 start];
24 }
25 
26 - (void)demo {
27   for (int i = 0; i < 10; ++i) {
28     // 堆棧大小
29     NSLog(@"%@ 堆棧大小:%tuK", [NSThread currentThread],[NSThread currentThread].stackSize / 1024);
30   }
31 
32    // 模擬崩潰
33    // 判斷是否是主線程
34       if (![NSThread currentThread].isMainThread) {
35           NSMutableArray *a = [NSMutableArray array];
36           [a addObject:nil];
37       }
38 }

 

4.2-3.0 線程狀態/線程生命周期 

  • 新建狀態
  • 就緒狀態/啟動狀態 : 線程在可調度線程池中
  • 運行狀態
  • 阻塞狀態/暫停線程 : 線程不在可調度線程池中,但是仍然存在記憶體中,只是不可用
  • 死亡狀態 : 線程不在記憶體中
  (1)新建狀態 : 實例化線程對象 說明:創建線程有多種方式,這裡不做過多的介紹     (2)就緒狀態 / 啟動線程: ( 進入就緒狀態 ->運行狀態。當線程任務執行完畢,自動進入死亡狀態 )
線程開啟 : 線程進入可調度線程池  
  • 向線程對象發送 start 消息,線程對象被加入可調度線程池等待 CPU 調度
  • detachNewThreadSelector 方法和 performSelectorInBackground 方法會直接實例化一個線程對象並加入可調度線程池

(3)運行狀態:
  • CPU 負責調度可調度線程池中線程的執行
  • 線程執行完成之前(死亡之前),狀態可能會在就緒和運行之間來回切換
  • 就緒和運行之間的狀態變化由 CPU 負責,程式員不能幹預
 
  • 當 CPU 調度當前線程 , 進入運行狀態
  • 當 CPU 調度其他線程 , 進入就緒狀態
(4)阻塞狀態 / 暫停線程 : 當滿足某個預定條件時,可以使用休眠或鎖阻塞線程執行
 1 方法執行過程,符合某一條件時,可以利用 sleep 方法讓線程進入 阻塞 狀態
 2 
 3 sleepForTimeInterval:    //休眠指定時長    (從現在起睡多少秒)
 4 sleepUntilDate:          //休眠到指定日期 (從現在起睡到指定的日期)
 5 @synchronized(self) { }   //互斥鎖
 6 
 71)阻塞2秒
 8 [NSThread sleepForTimeInterval:2]; // 阻塞狀態
 9 
102)以當前時間為基準阻塞4秒
11 NSDate *date = [NSDate dateWithTimeIntervalSinceNow:4.0]; //從現在開始多少秒
12 [NSThread sleepUntilDate:date];  //睡眠多少秒

(5)死亡狀態 (一旦線程停止或死亡了,就不能再次開啟任務 , 後續的所有代碼都不會被執行 )
(1) 正常死亡
  • 線程執行完畢
  (2) 非正常死亡
  • (自殺)          當滿足某個條件後,線上程內部自己中止執行
  • (被逼著死亡) 當滿足某個條件後,在主線程給其它線程打個死亡標記(下聖旨),讓子線程自行了斷.
  註意:在終止線程之前,應該註意釋放之前分配的對象! 
   生命周期示意圖:      代碼示例:
 1 #import "ViewController.h"
 2 
 3 @interface ViewController ()
 4 @end
 5 
 6 @implementation ViewController
 7 
 8 - (void)viewDidLoad {
 9   [super viewDidLoad];
10   // Do any additional setup after loading the view, typically from a nib.
11 }
12 
13 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
14 
15   // 1.在主線程中創建一個子線程(實例化線程對象) ---> 新建狀態
16   NSThread *Th =
17       [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
18 
19   // 2.將 Th 線程加入到可調度線程池,等待CPU調度--->就緒狀態
20   [Th start];
21 
22   // 3.讓主線程阻塞,讓當前線程(主線程)休眠
23   [NSThread sleepForTimeInterval:1.0];
24 
25   // 4.在主線程給 Th 線程打死亡標簽
26   [Th cancel]; //只是打了個標簽,並沒有執行,需要在子線程中
27 }
28 
29 // Th 線程---> 運行狀態
30 - (void)run {
31 
32   NSThread *huThread = [NSThread currentThread];
33 
34   CGMutablePathRef path = CGPathCreateMutable();
35 
36   for (int i = 0; i < 30; i++) {
37     if ([huThread isCancelled]) {
38       NSLog(@"good bye1");
39       return; // --->非正常死亡(被逼著死亡)
40     }
41 
42     if (i == 5) {
43       [NSThread sleepForTimeInterval:3.0]; //--->huThread阻塞狀態3秒
44       // [NSThread sleepUntilDate:[NSDate distantFuture]]; // 睡到遙遠的未來
45       // [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; //線程睡到從現在開始後的2秒為止
46     }
47 
48     if ([huThread isCancelled]) {
49       NSLog(@"good bye2");
50       return;
51     }
52 
53     if (i == 20) {
54       //清空資源
55       CGPathRelease(path);
56 
57       //在調用下麵方法之前,必須清空資源  非正常死亡--自殺(退出線程)
58       [NSThread exit];
59     }
60 
61     if ([huThread isCancelled]) {
62       NSLog(@"good bye3");
63       return;
64     }
65     NSLog(@"%d", i);
66   }
67 } //--->huThread死亡狀態  (正常死亡狀態)
68 
69 - (void)didReceiveMemoryWarning {
70   [super didReceiveMemoryWarning];
71   // Dispose of any resources that can be recreated.
72 }
73 
74 @end

 

4.2-4.1 多線程安全隱患 - 資源共用/搶奪

(1) 起因 :    資源共用概念 : 1塊資源可能會被多個線程共用,也就是多個線程可能會訪問同一塊資源   主要是因為多條線程,對`同一資源同時操作`,導致的問題
(2) 舉例 : 比如多個線程訪問同一個對象、同一個變數、同一個文件
(3) 結果 : 當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題 (資源強奪)
(4) 解決方案 : 互斥鎖 / 同步鎖 

代碼示例 :  (賣票案例)
多線程開發的複雜度相對較高,在開發時可以按照以下套路編寫代碼:
  • 首先確保單個線程執行正確
  • 添加線程
 1 #import "ViewController.h"
 2 
 3 @interface ViewController ()
 4 @property(nonatomic, strong) NSThread *thread01; // 售票員01
 5 @property(nonatomic, strong) NSThread *thread02; // 售票員02
 6 @property(nonatomic, strong) NSThread *thread03; // 售票員03
 7 
 8 @property(nonatomic, assign) NSInteger ticketCount; //票的總數
 9 @end
10 
11 @implementation ViewController
12 
13 - (void)viewDidLoad {
14   [super viewDidLoad];
15   // Do any additional setup after loading the view, typically from a nib.
16 
17   self.ticketCount = 10;
18 
19   //創建線程
20   self.thread01 = [[NSThread alloc] initWithTarget:self
21                                           selector:@selector(saleTicket)
22                                             object:nil];
23   self.thread01.name = @"售票員01";
24 
25   self.thread02 = [[NSThread alloc] initWithTarget:self
26                                           selector:@selector(saleTicket)
27                                             object:nil];
28   self.thread02.name = @"售票員02";
29 
30   self.thread03 = [[NSThread alloc] initWithTarget:self
31                                           selector:@selector(saleTicket)
32                                             object:nil];
33   self.thread03.name = @"售票員03";
34 }
35 
36 // 開啟線程
37 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
38   [self.thread01 start];
39   [self.thread02 start];
40   [self.thread03 start];
41 }
42 
43 // 賣票
44 - (void)saleTicket {
45 
46   while (1) {
47 
48     @synchronized(self) { //互斥鎖(控制器做鎖對象)
49       // 先取出總數
50       NSInteger count = self.ticketCount;
51 
52       // 判斷還有沒有餘票
53       if (count > 0) {
54         self.ticketCount = count - 1;
55         NSLog(@"%@賣了一張票,還剩下%zd張", [NSThread currentThread].name,
56               self.ticketCount);
57       } else {
58         NSLog(@"票已經賣完了");
59         break;
60       }
61     }
62   }
63 }
64 
65 - (void)didReceiveMemoryWarning {
66   [super didReceiveMemoryWarning];
67   // Dispose of any resources that can be recreated.
68 }

              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 【Objective-C Runtime動態載入】 動態創建類Class 動態創建類Class,動態添加Class成員變數與成員函數,動態變數賦值與取值,動態函數調用等方法 a.使用objc_allocateClassPair創建一個類Class const char * className = " ...
  • 什麼是主線程? 一個iOS程式運行後,預設會開啟一條線程,稱為“主線程”或“UI線程” 主線程的主要作用 1.顯示/刷新UI界面 2.處理UI事件(比如點擊事件,滾動事件,拖拽事件) 主線程的使用註意 1.別將比較耗時的操作放在主線程中 2.耗時操作會卡在主線程中,嚴重影響UI的流暢程度 如圖,將耗 ...
  • DrawerLayout是V4包下提供的一種左滑右滑抽屜佈局效果。 實現效果如下: 因為是官方提供的,所以使用起來也相對的比較簡單。 DrawerLayout 提供 1、當界面彈出的時候,主要內容區會自動背景變黑,當點擊內容區的時候,抽屜佈局會消失 2、在屏幕邊緣手勢滑動 會拉出抽屜佈局 註意:當按 ...
  • 圖片輪播是一種常見的自定義控制項,也有多種實現的方法,這裡提供一種簡單的ViewPager實現的案例。 實現功能:圖片迴圈輪播,進度顯示,圖片點擊事件 實現只需三步: 1 添加類文件到項目中。 2 在xml佈局中引入標簽。 3 實例化並設置簡單參數。 具體使用方法,詳見代碼註釋。 ==========... ...
  • 1.先來看看效果,這裡做了三個功能 2.實現app之間的跳轉需要註意兩方面 3首先來講url和白名單的設置 4.實現跳轉的代碼 5.demo:https://github.com/TigerCui/MyAppJumpToYourApp.git ...
  • 在學習多線程之前需要瞭解什麼是進程? 進程是指在系統中正在運行的一個應用程式 每個進程之間是獨立的,每個進程均運行在其專用且受保護的記憶體空間內 可以通過“活動監視器”查看Mac中所開啟的進程 下一個問題什麼是線程? 一個進程想要執行任務,必須得有線程(每一個進程只收要有一個主線程) 一個進程中的所有 ...
  • 什麼是讀寫鎖 讀寫鎖是多線程下的一種同步機制, 它還有很多其他的名字,比如共用 獨占鎖等等。被它保護的資源有以下幾種狀態: 初始狀態 沒有線程訪問資源時所在的狀態 共用狀態 有多個讀線程訪問資源時進入共用狀態,這個時候允許新的讀線程訪問資源,但是寫線程必須等待 獨占狀態 寫線程訪問資源時進入獨占狀態 ...
  • 該篇文章是我自己從我的新浪博客上摘抄過來的, 原文鏈接為: http://blog.sina.com.cn/s/blog_dcc636350102wat5.html 在iOS開發中, 難免會集成別人的三方類庫, 當集成的三方類庫過多時, 難免會出現某些庫同時使用了同樣的函數庫,導致link的時候報錯 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...