博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Thread(线程)四
阅读量:5296 次
发布时间:2019-06-14

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

今天继续讲讲线程的异常处理、线程取消、多线程的临时变量和线程安全lock的问题。

1、异步处理。

一般来说如果是同步方法的异步处理,我们大多都是try catch住,但是异步方法应该怎么做呢。

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
29
30
#region 异常处理
 
            
//多线程的委托是不允许异常的, try catch包住,写下日志
            
for 
(
int 
i = 0; i < 20; i++)
            
{
                
string 
name = 
string
.Format(
"btnThreadCore_Click{0}"
, i);
                
Action<
object
> act = t =>
                
{
                    
try
                    
{
                        
Thread.Sleep(2000);
                        
if 
(t.ToString().Equals(
"btnThreadCore_Click11"
))
                        
{
                            
throw 
new 
Exception(
string
.Format(
"{0} 执行失败"
, t));
                        
}
                        
if 
(t.ToString().Equals(
"btnThreadCore_Click12"
))
                        
{
                            
throw 
new 
Exception(
string
.Format(
"{0} 执行失败"
, t));
                        
}
                        
Console.WriteLine(
"{0} 执行成功"
, t);
                    
}
                    
catch 
(Exception ex)
                    
{
                        
Console.WriteLine(ex.Message);
                    
}
                
};
               
taskList.Add(taskFactory.StartNew(act, name));
            
}
           
Task.WaitAll(taskList.ToArray());
            
#endregion

  

2、线程取消。

Task不能主动取消,就好比向CPU发起了一个请求,但是你中途想中断这个请求,在正常情况下是做不到的,

同样,线程也做不到这一点,只有通过检测信号量的方式,来检测,使其线程本身来做。

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
29
30
31
32
33
34
35
36
37
CancellationTokenSource cts = 
new 
CancellationTokenSource();
 
              
for 
(
int 
i = 0; i < 40; i++)
              
{
                  
string 
name = 
string
.Format(
"btnThreadCore_Click{0}"
, i);
                  
Action<
object
> act = t =>
                  
{
                      
try
                      
{
                          
                          
Thread.Sleep(2000);
                          
if 
(t.ToString().Equals(
"btnThreadCore_Click11"
))
                          
{
                              
throw 
new 
Exception(
string
.Format(
"{0} 执行失败"
, t));
                          
}
                          
if 
(t.ToString().Equals(
"btnThreadCore_Click12"
))
                          
{
                              
throw 
new 
Exception(
string
.Format(
"{0} 执行失败"
, t));
                          
}
                          
if 
(cts.IsCancellationRequested)
                          
{
                              
Console.WriteLine(
"{0} 放弃执行"
, t);
                          
}
                          
else
                          
{
                              
Console.WriteLine(
"{0} 执行成功"
, t);
                          
}
                      
}
                      
catch 
(Exception ex)
                      
{
                          
cts.Cancel();
                          
Console.WriteLine(ex.Message);
                      
}
                  
};
                  
taskList.Add(taskFactory.StartNew(act, name);
//没有启动的任务  在Cancel后放弃启动
              
}
              
Task.WaitAll(taskList.ToArray());

  

通过代码运行可以看到会出现三种结果,那么这三种结果是什么情况下出现的呢,

执行成功和执行失败这两种情况应该好理解,,放弃执行是在执行失败出现时,捕获住了异常信息,然后通过cts.Cancel();使信号量改变,

然后通过cts.IsCancellationRequested判断,这就出现了只要是出现了执行失败,后面都是放弃执行的情况。

3、多线程临时变量

1
2
3
4
5
6
7
8
9
10
for 
(
int 
i = 0; i < 5; i++)
     
{
       
         
new 
Action(() =>
         
{
             
//Thread.Sleep(100);
             
Console.WriteLine(i);
           
         
}).BeginInvoke(
null
null
);
     
}

执行这么一段关键代码,会出现什么样的结果呢。

出现5个5,这是为什么呢,怎么和我们想的不一样,按理说不应该是出现0、1、2、3、4这样的数?

这里因为for循环是一定会比线程调用快,每一遍循环完,只是提交了线程,还没有调用,当调用时,循环已经结束,额调用时只会取最后i的值。

这就会出现5个5的情况,那么如何才能出现我们想要的结果呢。

1
2
3
4
5
6
7
8
9
10
for 
(
int 
i = 0; i < 5; i++)
{
    
int 
k = i;
    
new 
Action(() =>
    
{
        
Thread.Sleep(100);
       
// Console.WriteLine(i);
        
Console.WriteLine(k);
    
}).BeginInvoke(
null
null
);
}

只需要在循环体中加一个变量存储i的值,就可以了。 

4、线程安全 lock

关于线程安全,有的人太过于重视,而也有的人一点也不关心。那么我们应该怎么做线程安全呢。

1
2
3
4
5
6
7
8
9
10
11
private 
int 
TotalCount = 0;             <br>               
for 
(
int 
i = 0; i < 10000; i++)
{
   
    
taskList.Add(taskFactory.StartNew(() =>
        
{                        
                
this
.TotalCount += 1;                    
        
}));
}
Task.WaitAll(taskList.ToArray());
 
Console.WriteLine(
this
.TotalCount);

先来看看这段代码,可能大多数人会认为结果会是10000这样的结果,但是事实呢

你会发现,第一次是9997,第二次9998,第三次是9996,没有一次出现我们想要的结果。这又是为什么,

因为我们声明的private int TotalCount = 0,是共有变量,所有的线程都是调用同一个,这就出现了线程安全的问题。

那么我们应该如何解决这种情况呢,这就要加一把锁。

                           private static object btnThreadCore_Click_Lock = new object();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<em id=
"__mceDel"
>               
for 
(
int 
i = 0; i < 10000; i++)
                
{
               
                    
taskList.Add(taskFactory.StartNew(() =>
                        
{
                            
                            
lock 
(btnThreadCore_Click_Lock)
                            
{
                                
this
.TotalCount += 1;
                               
                            
}
                        
}));
                
}
                
Task.WaitAll(taskList.ToArray());
 
                
Console.WriteLine(
this
.TotalCount);
              
</em> 

这样在执行相加的时候只会允许一个线程进行相加。

 

讲完这四点,再来说说Await/Async,这两个一般都是同时出现

 1、只出现Async,会出现一个警告,合普通线程没什么区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private 
static 
async 
void 
NoReturnNoAwait()
      
{
          
//主线程执行
          
Console.WriteLine(
"NoReturnNoAwait Sleep before Task,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
          
Task task = Task.Run(() =>
//启动新线程完成任务
          
{
              
Console.WriteLine(
"NoReturnNoAwait Sleep before,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
              
Thread.Sleep(3000);
              
Console.WriteLine(
"NoReturnNoAwait Sleep after,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
          
});
 
          
//主线程执行
          
Console.WriteLine(
"NoReturnNoAwait Sleep after Task,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
      
}

  

同时出现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private 
static 
async 
void 
NoReturn()
{
    
//主线程执行
    
Console.WriteLine(
"NoReturn Sleep before await,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
    
TaskFactory taskFactory = 
new 
TaskFactory();
    
Task task = taskFactory.StartNew(() =>
    
{
        
Console.WriteLine(
"NoReturn Sleep before,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
        
Thread.Sleep(3000);
        
Console.WriteLine(
"NoReturn Sleep after,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
    
});
    
await task;
    
//子线程执行   其实是封装成委托,在task之后成为回调(编译器功能  状态机实现)
    
Console.WriteLine(
"NoReturn Sleep after await,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
}

  

带有await时,后面执行时,会发现也是子线程在执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private 
static 
async Task NoReturnTask()
     
{
         
//这里还是主线程的id
         
Console.WriteLine(
"NoReturnTask Sleep before await,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
 
         
Task task = Task.Run(() =>
         
{
             
Console.WriteLine(
"NoReturnTask Sleep before,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
             
Thread.Sleep(3000);
             
Console.WriteLine(
"NoReturnTask Sleep after,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
         
});
         
await task;
         
Console.WriteLine(
"NoReturnTask Sleep after await,ThreadId={0}"
, Thread.CurrentThread.ManagedThreadId);
 
         
//return new TaskFactory().StartNew(() => { });  不能return
     
}

  这种和前面的没什么区别,只不过它带有返回值Task。其运行结果是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private 
static 
async Task<
long
> SumAsync()
      
{
          
Console.WriteLine(
"SumAsync {1} start ManagedThreadId={0}"
, Thread.CurrentThread.ManagedThreadId, 111);
          
long 
result = 0;
 
          
await Task.Run(() =>
          
{
              
for 
(
int 
k = 0; k < 10; k++)
              
{
                  
Console.WriteLine(
"SumAsync {1} await Task.Run ManagedThreadId={0}"
, Thread.CurrentThread.ManagedThreadId, k);
                  
System.Threading.Thread.Sleep(1000);
              
}
 
              
for 
(
long 
i = 0; i < 999999999; i++)
              
{
                  
result += i;
              
}
          
});
          
Console.WriteLine(
"SumAsync {1} end ManagedThreadId={0}"
, Thread.CurrentThread.ManagedThreadId, 111);
          
return 
result;
      
}

  如果是带有返回值的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private 
static 
async Task<
long
> SumAsync()
{
    
Console.WriteLine(
"SumAsync {1} start ManagedThreadId={0}"
, Thread.CurrentThread.ManagedThreadId, 111);
    
long 
result = 0;
 
    
await Task.Run(() =>
    
{
   
        
for 
(
long 
i = 0; i < 999999999; i++)
        
{
            
result += i;
        
}
    
});
    
Console.WriteLine(
"SumAsync {1} end ManagedThreadId={0}"
, Thread.CurrentThread.ManagedThreadId, 111);
    
return 
result;
}

  

1
2
3
4
5
Task<
long
> t = SumAsync();
 
long 
lResult = t.Result;
//访问result   主线程等待Task的完成
//t.Wait();//等价于上一行
Console.WriteLine(lResult);

  

 

转-https://www.cnblogs.com/xima/p/7285960.html

转载于:https://www.cnblogs.com/asdyzh/p/9905828.html

你可能感兴趣的文章
cxGrid主从表删除从表记录的困惑
查看>>
【Leetcode 167】Two Sum II - Input array is sorted
查看>>
.Net Core中的通用主机(二)——托管服务
查看>>
log4j.properties
查看>>
路由器防火墙配置命令
查看>>
何去何从,还是不知所措
查看>>
Linux系统开机启动流程
查看>>
学习进度条(第六周)
查看>>
[ajax] quick double or multiple click ajax submit cause chrome explorer's error snatshot
查看>>
#Leetcode# 32. Longest Valid Parentheses
查看>>
CodeForces Round #521 (Div.3) C. Good Array
查看>>
实训作业---I/O流
查看>>
数据包各层首部
查看>>
Cracking the Oyster(Column 1 of Programming Pearls)
查看>>
Oracle OEM 配置报错: No value was set for the parameter DBCONTROL_HTTP_PORT 解决方法
查看>>
出个题目给大家,能解决的才是真正的懂js,答案会在以后公布
查看>>
CSMA/CD && CSMA/CA
查看>>
学习js第二天小结
查看>>
vue methods 中方法的相互调用
查看>>
负载均衡
查看>>