博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c#之多线程之为所欲为
阅读量:7211 次
发布时间:2019-06-29

本文共 4582 字,大约阅读时间需要 15 分钟。

一 什么是多线程

1、 什么是进程?一个 exe 运行一次就会产生一个进程,一个 exe 的多个进程之 间数据互相隔离。

2、 一个进程里至少有一个线程:主线程。我们平时写的控制台程序默认就是单线程的,代 码从上往下执行,一行执行完了再执行下一行;

3、 什么是多线程:一个人两件事情同时做效率高。同一时刻一 个人只能干一件事情,其实是在“快速频繁切换”,如果处理不当可能比不用多线程效率还低

 

 

二  Thread 对象

2.1 thread基础写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
        public 
static 
void 
ThreadTest()
        
{
            
int 
a = 0;
            
Thread thread1 = 
new 
Thread(m=>
            
{
                
for 
(
int 
i = 0; i < 20; i++)
                
{
                    
a = a + 1;
                    
Console.WriteLine(
"线程1:"
+ a);
                
}
 
            
});
 
            
Thread thread2 = 
new 
Thread(m =>
            
{
            
for 
(
int 
i = 0; i < 20; i++)
            
{
                    
a = a + 1;
                
Console.WriteLine(
"线程2:"
+ a);
                
}
 
            
});
             
            
thread1.Start();
            
thread2.Start();
            
Console.ReadKey();
        
}

这段代码输出结果如下:

可以看出两个子线程启动后是并行执行的,所以输出结果没有按照顺序来

 

2.2 设置线程的优先级

thread1.Priority=ThreadPriority。。。

2.3  t1.Join()当前线程等待 t1 线程执行结束,实例如下:

这段代码执行过后输出的结果就是正常的从1输出到了40

 
public static void ThreadTest()        {            int a = 0;            Thread thread1 = new Thread(m=>             {                for (int i = 0; i < 20; i++)                {                    a = a + 1;                    Console.WriteLine("线程1:"+ a);                }            });            Thread thread2 = new Thread(m =>            {                //等待thread1线程任务完成后在执行                thread1.Join();                for (int i = 0; i < 20; i++)                {                    a = a + 1;                Console.WriteLine("线程2:"+ a);                }            });            //可以将参数传入到子线程中            thread1.Start(a);            //thread1.Join(); 或者将Join放在这里            thread2.Start(a);            Console.ReadKey();        }

 

2.4 Interrupt方法

Interrupt 用于提前唤醒一个在 Sleep 的线程,Sleep 方法会抛出 ThreadInterruptedException 异常 代码如下: 

代码输出到9的时候线程会休眠8秒钟,但是运行到主线程thread1.Interrupt()时,子线程会被唤醒,然后执行catch里面的Console.WriteLine("线程被唤醒");之后接着从10开始输出到2000。需要注意的是只有线程自身能让自身休眠

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public 
static 
void 
ThreadTest2()
        
{
            
Thread thread1 = 
new 
Thread(() =>
            
{
                
for 
(
int 
i = 0; i < 2000; i++)
                
{
                    
if 
(i==10)
                    
{
                        
//唤醒线程之后会引发ThreadInterruptedException类型的异常,所以需要try catch
                        
try
                        
{
                            
//子线程休眠8秒钟
                            
Thread.Sleep(8000);
                        
}
                        
catch 
(ThreadInterruptedException ex)
                        
{
                            
Console.WriteLine(
"线程被唤醒"
);
                        
}
                    
}
                    
Console.WriteLine(i);
                
}
            
});
            
thread1.Start();
            
//提前唤醒在沉睡的子线程
            
Thread.Sleep(3000);
            
thread1.Interrupt();
            
Console.ReadKey();
        
}

  

三 线程池

3.1、线程池:因为每次创建线程、销毁线程都比较消耗 cpu 资源,因此可以通过线程池进行 优化。线程池是一组已经创建好的线程,随用随取,用完了不是销毁线程,然后放到线程池 中,供其他人用。

3.2、用线程池之后就无法对线程进行精细化的控制了(线程启停、优先级控制等)

3.3、ThreadPool 类的一个重要方法:

  static bool QueueUserWorkItem(WaitCallback callBack)

  static bool QueueUserWorkItem(WaitCallback callBack, object state)

3.4、除非要对线程进行精细化的控制,否则建议使用线程池,因为又简单、性能调优又更好。

1
2
3
4
5
6
7
8
9
10
11
12
13
//QueueUserWorkItem是一个静态方法不需要New
public 
static 
void 
ThreadPool()
{
    
System.Threading.ThreadPool.QueueUserWorkItem(m=>
    
{
        
for 
(
int 
i = 0; i < 1000; i++)
        
{
            
i++;
            
Console.WriteLine(i);
        
}
    
});
    
Console.ReadKey();
}

  

四 TPL风格的异步方法

TPL(Task Parallel Library)是.Net 4.0 之后带来的新特性,更简洁,更方便。现在在.Net 平台下已经大面积使用。

注意方法中如果有 await,则方法必须标记为 async,不是所有方法都可以被轻松的标记 为 async。WinForm 中的事件处理方法都可以标记为 async、MVC 中的 Action 方法也可以标 记为 async、控制台的 Main 方法不能标记为 async。  TPL 的特点是:方法都以 XXXAsync 结尾,返回值类型是泛型的 Task<T>。  TPL 让我们可以用线性的方式去编写异步程序,不再需要像 EAP 中那样搞一堆回调、逻 辑跳来跳去了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//TPL风格返回的Task<T> 泛型的数据
//await 关键字等待异步方法返回
public 
static 
async 
void 
Task()
{
    
WebClient wc = 
new 
WebClient();
    
string 
s= await wc.DownloadStringTaskAsync(
"http://www.baidu.com"
);
    
Console.WriteLine(s);
    
Console.ReadKey();
}
public 
static 
void 
Task2()
{
    
WebClient wc = 
new 
WebClient();
    
//若果不使用await关键字就得使用Task<string>类型来接收数据
    
Task<
string
> s2 = wc.DownloadStringTaskAsync(
"http://www.baidu.com"
);
    
Console.ReadKey();
}

  

自己编写一个TPL风格的异步方法:

使用了async关键字就必须返回Task泛型数据类型的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public 
static 
Task<
string
> StringAsync()
{
   
return 
Task.Run(() =>
    
{
        
Thread.Sleep(5000);
        
return 
"hehe"
;
    
});
           
}
// GET: Home
public 
async Task<ViewResult> Index()
{
 
    
var 
s = await StringAsync();
    
return 
View();
}

  

如果返回值就是一个立即可以随手可得的值,那么就用 Task.FromResult()  如果是一个需要休息一会的任务(比如下载失败则过 5 秒钟后重试。主线程不休息,和 Thread.Sleep 不一样),那么就用 Task.Delay()。 3、Task.Factory.FromAsync()把 IAsyncResult 转换为 Task,这样 APM 风格的 api 也可以用 await 来调 用。 4、编写异步方法的简化写法。如果方法声明为 async,那么可以直接 return 具体的值,不再用创建 Task,由编译器创建 Task:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static 
async Task<
int
> F1Async()
{
     
return 
1;
 
}
  
static 
Task<
int
> F2Async()
     
return 
Task.FromResult(3);
}
  
static 
Task<
int
> F3Async()
     
return 
Task.Run(()=> { 
     
return 
1 + 3;  });
}

一定要让 async 的传染性(调用异步方法要用 await,用了 await 方法就要声明为 async,调 用我这个 async 方法的地方必须要 await……)不要轻易直接调用 Task 的 Wait、WaitAll 等方 法。等待一个用 await,而不是 task.Wait();等待多个用 await Task.WhenAll(),而不是 Task.WaitAll()

转载地址:http://xsrum.baihongyu.com/

你可能感兴趣的文章
Theano3.3-练习之逻辑回归
查看>>
利用RGB-D数据进行人体检测 带dataset
查看>>
live555的编译及使用
查看>>
C++builder XE 安装控件 及输出路径
查看>>
优点和阵列的缺点,并且一个链表
查看>>
CSS3透明属性opacity
查看>>
Genymotion模拟器的安装及常见问题解决方法
查看>>
PHP 乘法口诀表
查看>>
如何彻底关闭windows update
查看>>
SpringMVC+SwfUpload进行多文件同时上传
查看>>
ASP.NET Core中的依赖注入(2):依赖注入(DI)
查看>>
Java_JAVA6动态编译的问题
查看>>
scala 日期格式转换
查看>>
Filtering Specific Columns with cut
查看>>
多线程编程1-NSThread
查看>>
反馈组态的判别
查看>>
【Web】Rest API 验证授权如何做?
查看>>
Swift 中的 @autoclosure
查看>>
多迪将企业的Python工程师定位成哪几类?
查看>>
Rom 检测
查看>>