线程学习笔记

多线程网络的学习

什么是进程?

  系统中正在运行的一个应用程序是一个进程。

  比如同时打开qq xcode系统就会分别启动两个进程。而且是相对独立的进程,相互不影响。

  1个进程想要执行任务,就必须有线程,每个进程至少有1个线程。

  线程是进程的基本执行单元,一个进程的所有任务都在线程中执行。

串行:

  1个线程中的任务是串行(顺序执行)的。

  如果在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。

  也就是说同一时间内,1个线程只能执行1个任务。

  比如下载任务。

 

多线程

  1个进程中可以开启多个线程,每个线程可以并发(同时)执行不同任务。

  例如:电脑可以打开扣扣聊天,可同时听音乐,浏览网页等。

多线程原理:

  同一时间,cpu只能处理1条线程,只有一个线程在工作(执行)。

  线程并发执行,其实就是cpu快速地在多条线程之间切换。——-也就是每个线程每次都会执行一点,直至所有进程都执行完毕。

  若果cpu调度线程地时间足够快,就造成了多线程地并发执行地假象。

思考:如果线程非常多,会发生什么情况?

  Cpu会在n条线程直接切换,cpu会累死,消耗大量地cpu资源

 

多线程地优点

  1.提高程序执行效率

  2.能适当提高资源利用率

 

多线程缺点

  1.开启线程需要占用一定地内存空间(默认情况下 主线程占用1m 子线程占用512mb)如果开启大量线程,会占用大量地内存空间,降低程序地性能。

  2.线程越多,cpu在调度线程上地开销就越大。

  3.程序设计更加复杂:比如线程之间地通信、多线程地数据共享。

 

什么是主线程?

  一个ios程序运行后默认会开启1个线程,成为主线程或ui线程。跟ui相关地操作都放在主线程。

 

主线程地主要作用:

  1.显示/刷新ui界面。

  2.处理ui事件(点击、滚动、拖拽)

主线程使用注意:

  别讲比较耗时地操作放到主线程中(比如请求网络这个操作就不要放在主线程中)

  耗时操作,会卡住主线程,严重影响ui流畅度,给用户一种“卡”地坏体验。

 

  打印出当前所在地线程  number == 1 主线程    number!= 1 子线程 次线程 其他线程。

  NSLog(@”%@”,[NSThread currentThread]);

 

  将耗时操作放到子线程中去,也就是为耗时操作开辟一个新的线程空间让其在里边运行。 LongTime是方法名称,它就是在子线程中运行地。  Withobject是参数。

  [self  performSelectorInBackground:@selector(longTime) withObject:nil];

ios中地多线程实现方案:

  1. pthread :————程序员管理,几乎不用它是底层地  c语言

  • 一套通用地多线程api
  • 适用于unix/linux/win等系统
  • 跨平台/可移植
  • 使用难度大

  2. NSThread 程序员管理 偶尔使用  oc

  • 使用更加面向对象
  • 更简单宜用,直接操作线程对象

  3.GCD   自动管理 经常使用  c语言

  • 旨在代替NSThread等线程技术
  • 充分利用设备地多核

4.NSOperation 自动管理  经常使用  oc语言

  • 基于GCD(底层是GCD)
  • 比GCD多了些简单实用功能
  • 使用更加面向对象

 使用pthread必须引入  import<pthread.h>这个头文件。

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
  [self text1];
}
//
void * 必须return -void *run(void *param) { //耗时操作在这里执行。 for(int i=0; i< 200000; i ++) { NSLog(@"%@",[NSthread currentThread]); } return NULL; } -(void)test1 { pthread_t threadId; //参数: 1.要开的线程的变量 2.线程的属性 3.要在开辟的子线程要执行的函数(函数) 4.这个字线程任务需要传递的参数。 pthread_create(&threadId,NULL,run,NULL); }

void*  相当于oc 语言中的id 可以指向任意变量/对象。

NSThread的使用方法:

  一个NSThread就代表一个线程。

c

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
  [self text1];
}

-(void)run
{
    for(int i = 0;i<10;i++)
   {
      NSLog(@"%@",[NSThread currentThread]);
   }
}
// 创建线程方式 1.
-(void)test1
{
   // 实例化一个线程对象
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
  // 让线程开始工作,启动线程,在新开的线程中,执行run方法。
   [thread start];
}
-(void)run2:(NSString *)str
{
    for(int i = 0;i<10;i++)
   {
      NSLog(@"%@",[NSThread currentThread]);
   }
}
// 创建线程方式2.
-(void)test2
{
   [  NSTread detachNewThreadSelector:@selector(run2:) toTarget:self withObject:@"hello"];
}

// 创建线程方式3
-('void)test3
{
  // “隐式”创建线程的方式。  它没有明确说出创建线程,而其实质就是创建新的线程。
 // 其余两种都是显式的创建线程。
  [self performSelectorInBackground:@selector(run:) withObject:"hello"];
}

-(void)test4
{
   // 实例化一个线程对象
    NSThread *threadA = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
    threadA.name = @"thread A";  // 线程的名称。
// 当前线程的优先级。一般情况下不会去设置 ,浮点型的取值,取值范围0.0~1.0 由低到高 ,默认是0.5
threadA.threadPriority = 0.1;
// 让线程开始工作,启动线程,在新开的线程中,执行run方法。 [threadA start];
    NSThread *threadB = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
    threadB.name = @"thread B";  // 线程的名称。
// 当前线程的优先级。一般情况下不会去设置 ,浮点型的取值,取值范围0.0~1.0 ,默认是0.5
threadB.threadPriority = 1.0;
// 让线程开始工作,启动线程,在新开的线程中,执行run方法。 [threadB start];
}
 

 主线程的相关方法:

+(NSThread *)mainThread; // 获得主线程

-(BOOL)isMainThread; // 是否为主线程

+(BOOL)isMainThread; // 是否为主线程(类方法)

 

线程的状态:

  1.新建状态  (New)

 NSThread *threadA = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];

  2.就绪状态(Runnable)

    [thread start]; // 这个时候cpu就可以调度当前线程了----当前线程被添加到可调用线程池中。--cpu 只会在这个池中调用线程。

  3.运行状态(Runing)

    cpu调用当前线程的时候当前线程就是-运行状态,如果cpu调用其他线程,那么当前线程就又返回成“就绪状态”;

  4.阻塞状态(Blocked)

   调用了sleep方法等待同步锁

   变成阻塞状态之后,当前线程就会从可调用线程池中被移除。cpu就找不到当前线程。

 当sleep到时得到同步锁,当前线程会重新添加到可调用线程池中。

在可调用线程池中线程只存在着两种状态:就绪---运行

  5.Dead

  线程任务执行完毕异常强制退出

  从可调用线程池中移除,然后从内存中销毁。-----一旦死亡,就不能再次开启任务。

启动线程

-(void)start     // 进入就绪状态->运行状态。当线程任务执行完毕,自动进入死亡状态。

阻塞(暂停)线程

+(void)sleepUntilDate:(NSDate *)date; // 睡到什么时候。

+(void)sleepForTimeInterval:(NSTimeInterval)time;// 进入阻塞状态   睡多长时间

+(void)exit; // 进入死亡状态 。

多线程存在着安全隐患,因为可能会出现多个线程同时访问同一块资源,出现资源错乱等现象。

eg:银行存款,可能会出现同一账号同时存取的情况。

这种情况下利用互斥锁(独占锁)进行可以解决当前问题,而互斥锁需要锁住执行状态,也就是关键代码只可一个线程访问。

@property (nonatomic ,strong)NSInteger tickets;

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
  [self text1];
}
// void *  必须return
-void *run(void *param)
{
  while(YES)
  {
    [NSThread sleepForTimeInterval:1.0];
// 确保synchronized的参数在多个线程访问的时候都是同一个对象。也就是说这个锁都是同一把锁。 @synchronized(self){
if(self. tickets>0) { self.tickets--; }else{NSLog(@"票售完了");break;} }; } return NULL; } -(void)test1 { NSThread *threadA = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil]; threadA name = @"售票员a" [threadA start]; NSThread *threadB = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil]; threadB.name = @"售票员b" [threadB start]; }

互斥锁锁定的代码一定要少。加锁范围内的代码同一时间只允许一个线程访问,互斥锁的参数:任何继承NSObject *对象都可以。要保证这个锁,所有的线程都能访问到。由于锁的性能太差,苹果官方不推荐使用。(消耗大量的cpu资源)而在ios开发中我们做的都是前端,所以不需要关心加锁问题,这个问题时由服务器进行解决的。

 

 

 

原文地址:https://www.cnblogs.com/pengpengzhang/p/4768742.html