Parallel并发执行多个Action,主线程会参与计算—阻塞界面,等于TaskWaitAll+主线程计算。
{
Parallel.Invoke(() => this.DoSomethingLong("btnParallel_Click_1"),
() => this.DoSomethingLong("btnParallel_Click_2"),
() => this.DoSomethingLong("btnParallel_Click_3"),
() => this.DoSomethingLong("btnParallel_Click_4"),
() => this.DoSomethingLong("btnParallel_Click_5"));
}
{
Parallel.For(0, 5, i => this.DoSomethingLong($"btnParallel_Click_{i}"));
}
{
Parallel.ForEach(new int[] { 0, 1, 2, 3, 4 }, i => this.DoSomethingLong($"btnParallel_Click_{i}"));
}
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 3; //最大线程数量
Parallel.For(0, 10, options, i => this.DoSomethingLong($"btnParallel_Click_{i}"));
}
主线程不参与计算,不阻塞界面。如下:
{
//不阻塞界面
Task.Run(() =>
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 3;
Parallel.For(0, 10, options, i => this.DoSomethingLong($"btnParallel_Click_{i}"));
});
}
在子线程内部,发生异常之后,异常找不到了,那么异常到那儿去了?必然是被吞掉了;
如何把异常捕捉到呢?
{
try
{
List<Task> taskList = new List<Task>();
for (int i = 0; i < 50; i++)
{
string name = $"btnTaskAdvanced_Click_{i}";
taskList.Add(Task.Run(() =>
{
if (name.Equals("btnTaskAdvanced_Click_8"))
{
throw new Exception("btnTaskAdvanced_Click_8 异常了。。。");
}
else if (name.Equals("btnTaskAdvanced_Click_13"))
{
throw new Exception("btnTaskAdvanced_Click_13 异常了。。。");
}
else if (name.Equals("btnTaskAdvanced_Click_25"))
{
throw new Exception("btnTaskAdvanced_Click_25 异常了。。。");
}
Console.WriteLine($"this is {name} 成功, ThreadID={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}));
}
Task.WaitAll(taskList.ToArray());
}
catch (AggregateException aex)
{
foreach (var aexception in aex.InnerExceptions)
{
Console.WriteLine(aexception.Message);
}
Console.WriteLine(aex.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
发生异常是个很尴尬的事儿,如果多个线程同时执行业务,如果有一个线程异常了,其实对整个业务链来说是不完美的;需要一些应对策略,可能就需要让其他的线程停止下来,重新操作;
多线程中如果有一个线程执行失败,需要取消其他的线程;
50个线程:其中有一个线程启动了,其他别的线程还没启动;刚好就这个启动的线程就异常了;还没有启动的这49个线程,我还有必要让你启动吗?
bool isOk = true;
for (int i = 0; i < 50; i++)
{
string name = $"btnTaskAdvanced_Click_{i}";
Task.Run(() =>
{
if (isOk)
{
Console.WriteLine($"{name} Start.....");
}
else
{
throw new AggregateException(); //停止了线程
}
if (name.Equals("btnTaskAdvanced_Click_8"))
{
isOk = false;
throw new Exception("btnTaskAdvanced_Click_8 异常了。。。");
}
else if (name.Equals("btnTaskAdvanced_Click_13"))
{
throw new Exception("btnTaskAdvanced_Click_13 异常了。。。");
}
else if (name.Equals("btnTaskAdvanced_Click_25"))
{
throw new Exception("btnTaskAdvanced_Click_25 异常了。。。");
}
if (isOk)
{
Console.WriteLine($"{name} End.....");
}
else
{
//这里也可以回滚
throw new AggregateException(); //停止了线程
}
Console.WriteLine($"this is {name} 成功, ThreadID={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
});
}
CancellationTokenSource类中有一个属性:IsCancellationRequested,默认值为false;
还有一个Cancel()方法,只要是Cancel()执行,就可以把IsCancellationRequested指定为true; 可以重复调用Cancel()方法;
跟全局变量有点像,CancellationTokenSource是线程安全的;Cancel() 调用以后IsCancellationRequested指定为true不能再重置回来的;
以上+业务判断来实现线程取消;如果在Cancel 之前已经进入业务处理的线程是无法停止下来,所以在最后再判断一次,不让你正常运行结束;
try
{
CancellationTokenSource cts = new CancellationTokenSource();
List<Task> taskList = new List<Task>();
for (int i = 0; i < 100; i++)
{
Thread.Sleep(new Random().Next(100, 300));
string name = $"btnTaskAdvanced_Click_{i}";
taskList.Add(Task.Run(() =>
{
//只要是执行到这里了,就是一个单独的线程;这里就可以开启事务
if (!cts.IsCancellationRequested)
{
Console.WriteLine($"{name} Start.....{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}
else
{
Console.WriteLine($"{name} 失败了.....{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
//可以事务回滚
//throw new AggregateException(); //停止了线程
}
if (name.Equals("btnTaskAdvanced_Click_8"))
{
cts.Cancel(); //就可以把IsCancellationRequested指定为true;
throw new Exception("btnTaskAdvanced_Click_8 异常了。。。");
}
else if (name.Equals("btnTaskAdvanced_Click_13"))
{
cts.Cancel(); //就可以把IsCancellationRequested指定为true;
throw new Exception("btnTaskAdvanced_Click_13 异常了。。。");
}
else if (name.Equals("btnTaskAdvanced_Click_25"))
{
cts.Cancel(); //就可以把IsCancellationRequested指定为true;
throw new Exception("btnTaskAdvanced_Click_25 异常了。。。");
}
//如果有业务需要,其实也可以直接Cancel(),其他的线程也都停止下来了;
if (!cts.IsCancellationRequested)
{
Console.WriteLine($"{name} End.....{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}
else
{
//这里也可以回滚
//throw new AggregateException(); //停止了线程
Console.WriteLine($"{name} 失败了.....{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}
//Console.WriteLine($"this is {name} 成功, ThreadID={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}, cts.Token)); //只需要把cts.Token 给Task.run()
}
Task.WaitAll(taskList.ToArray());
}
catch (AggregateException aex)
{
foreach (var exception in aex.InnerExceptions)
{
Console.WriteLine(exception.Message);
}
}
1.线程的开启是非阻塞的,延迟启动;
2.这里是循环5次,代码执行很快,开启线程不阻塞
for (int i = 0; i < 5; i++)
{
int k = i;
Task.Run(() =>
{
Console.WriteLine($"ThreadID={Thread.CurrentThread.ManagedThreadId.ToString("00")}_i={i}__ k={ k}");
});
}
如果你的代码在单线程情况下执行的结果和多线程执行的结果完全一致,那么这就是线程安全的;
线程安全问题一般发生在全局变量、共享变量、硬盘文件,只要是多线程都能访问和修改的公共数据;
因为是多线程操作,操作同时进行,可能会出现覆盖;
怎么解决线程安全:
List<int> tasklist = new List<int>();
BlockingCollection<int> blockinglist = new BlockingCollection<int>();
ConcurrentBag<int> conocurrentbag = new ConcurrentBag<int>();
ConcurrentDictionary<string, int> concurrentDictionary = new ConcurrentDictionary<string, int>();
ConcurrentQueue<int> concurrentQueue = new ConcurrentQueue<int>();
ConcurrentStack<int> concurrentStack = new ConcurrentStack<int>();
//OrderablePartitioner OrderablePartitioner = new OrderablePartitioner();
//Partitioner partitionerArray=new
//Partitioner
for (int i = 0; i < 10000; i++)
{
int k = i;
Task.Run(() =>
{
tasklist.Add(k);
blockinglist.Add(k);
conocurrentbag.Add(k);
concurrentDictionary.TryAdd($"concurrentDictionary_{k}", k);
concurrentQueue.Enqueue(k);
concurrentStack.Push(k);
});
}
Console.WriteLine($"tasklist:{tasklist.Count()}");
Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
Console.WriteLine($"blockinglist{blockinglist.Count()}");
Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
Console.WriteLine($"conocurrentbag{conocurrentbag.Count()}");
Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
Console.WriteLine($"concurrentDictionary{concurrentDictionary.Count()}");
Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
Console.WriteLine($"concurrentQueue{concurrentQueue.Count()}");
Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
Console.WriteLine($"concurrentStack{concurrentStack.Count()}");
Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
Lock,锁—排他性,独占;
标准锁:锁对象,引用类型,不要锁string,可能会冲突;
强烈规定:以后大家使用lock,按照这种标准格式;private static readonly object obj_Forom = new object(); int,string,this 都不要;
private static readonly object obj_Forom = new object();
{
List<int> tasklist = new List<int>();
for (int i = 0; i < 10000; i++)
{
int k = i;
Task.Run(() =>
{
lock (obj_Forom) //只允许一个线程从这里经过,这不就是单线程了吗? 反多线程;
{
tasklist.Add(k);
}
});
}
Console.WriteLine($"tasklist:{tasklist.Count()}");
}
线程安全问题一般发生在全局变量、共享变量、硬盘文件,只要是多线程都能访问和修改的公共数据;
既然多线程去操作会有线程安全问题,那么就拆分数据源,然后每一个线程对标于单独的某一个数据块;
多线程操作完毕以后,再合并数据。