• 背景:

一般情况下,经常会遇到一个单线程程序时执行对CPU,MEMORY,IO利用率上不来,且速度慢下问题;那么,怎么解决这些问题呢?

据我个人经验来说有以下两种方式:

1、并行、多线程(Parallel、Task、ThreadPool)

2、多进程MultipleProcess

恰好工作中又一次遇到单线程程序性能低的问题,本次我主要想尝试使用ThreadPool来实现多线程,并且在实现多线程任务同步结束。

  • ThreadPool线程同步结束示例一:

一个ManualResetEvent结合Interlocked来实现线程同步结束。

  static void Main(string[] args)
{
using (ManualResetEvent finish = new ManualResetEvent(false))
{
int maxThreadCount = ;
for (var i = ; i < ; i++) {
ThreadPool.QueueUserWorkItem((Object state)=> {
Console.WriteLine("task:{0}",state); // 以原子操作的形式递减指定变量的值并存储结果。
if (Interlocked.Decrement(ref maxThreadCount) == ) {
// 将事件状态设置为有信号,从而允许一个或多个等待线程继续执行。
finish.Set();
}
}, i);
} // 阻止当前线程,直到当前 System.Threading.WaitHandle 收到信号。
finish.WaitOne();
} Console.WriteLine("Complete!");
Console.ReadKey();

上边的代码是可行性,当系统线程数超过系统允许最大数时,线程会被在线程池中排队等待。

  • ThreadPool线程同步结束示例二:

ManualResetEvent集合(每一个线程由集合中的唯一一个ManualResetEvent对象来实现线程的同步跟踪)结合WaitHandle.WaitAll(ManualResetEvent集合)来实现线程同步结束。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading; namespace ThreadPoolTest
{
class MyTask
{
private ManualResetEvent finish = null;
public MyTask(ManualResetEvent finish)
{
this.finish = finish;
} public void MyTaskThreadPoolCallback(Object state)
{
Console.WriteLine("task:{0}", state); // 将事件状态设置为有信号,从而允许一个或多个等待线程继续执行。
this.finish.Set();
}
} class Program
{
static void Main(string[] args)
{
const int maxThreadCount = ;
ManualResetEvent[] finishItems = new ManualResetEvent[maxThreadCount];
MyTask[] myTaskItems = new MyTask[maxThreadCount]
;
for (var i = ; i < maxThreadCount; i++)
{
finishItems[i] = new ManualResetEvent(false); MyTask myTask = new MyTask(finishItems[i]);
myTaskItems[i] = myTask; ThreadPool.QueueUserWorkItem(myTask.MyTaskThreadPoolCallback, i);
} // 等待指定数组中的所有元素都收到信号。
WaitHandle.WaitAll(finishItems); Console.WriteLine("Complete!");
Console.ReadKey();
} }
}

尽管这种想法不错,但是存在一些问题:比如ManualResetEvent集合数量不允许超过系统允许的最大数量,我的计算机系统允许的最大数量是64,当我把配置超过64时(const int maxThreadCount = 65;),就会抛出异常。

  • 实现多线程时,需要注意事项:

可是一般情况下遇到这种业务的情况下,只要修改多线程,必然会遇到某个对象不允许被多个线程操作的问题。

比如:

1、多个线程同时向一个文件中写入内容,这种情况一般使用锁来包成被访问对象的安全性。比如:互斥锁(lock、Mutex)、读写锁(ReadWriteLock)、Monitor、Semaphore(信号灯)、Interlocked(内存共享)等。

2、多个线程同时修改一个非线程安全集合对象(List,Collection,Dictionary,Bag,Queue,Stack,ArrayList,Array,HashTable等)时,往往会抛出异常。针对这种情况,需要使用命名空间System.Collections.Concurrent.*下支持线程安全的集合、字典、队列、栈等对象来替代。

  • 业务场景:

我们需要对一个多行文本文件进行解析,根据具体地址解析其中的经纬度信息。如果解析过程中解析失败的行,需要记录到一个_error.txt;解析成功的记录行,记录到_result.txt。使用单线程分析过程中已经遇到了性能低问题,需求解决方案是使用ThreadPool技术。

  • 业务实现:
         private static int maxThreadCount = ;
private static int fakeMaxThreadCount = int.MaxValue;
private static ManualResetEvent finish = new ManualResetEvent(false);
private static object errorLocker = new object();
private static object resultLocker = new object();
private static object maxThreadCountLcker = new object(); public void ParserFile(string filePath)
{
using (StreamWriter writerError = new StreamWriter(filePath + "_error"))
{
using (StreamWriter writerResult = new StreamWriter(filePath + "_result"))
{
finish = new ManualResetEvent(false);
using (StreamReader reader = new StreamReader(filePath))
{
string line = reader.ReadLine();
while (line != null)
{
maxThreadCount++;
ThreadPool.QueueUserWorkItem(DoWork, new object[] { line, writerError, writerResult
}); line = reader.ReadLine();
}
} maxThreadCount++;
lock (maxThreadCountLcker)
{
fakeMaxThreadCount = maxThreadCount;
} ThreadPool.QueueUserWorkItem(DoWork, new object[] { }); finish.WaitOne();
finish.Close();
finish.Dispose();
}
}
} private void DoWork(object state)
{
object[] objectItem = state as object[];
if (objectItem.Length != )
{
if (Interlocked.Decrement(ref fakeMaxThreadCount) == )
{
finish.Set();
}
return;
}
string line = objectItem[].ToString();
StreamWriter writerError = objectItem[] as StreamWriter;
StreamWriter writerResult = objectItem[] as StreamWriter; try
{
string[] fields = line.Split(new char[] { '|' }); string imsi = fields[];
string city = fields[];
string county = fields[];
string address = fields[]; // http://restapi.amap.com/v3/geocode/geo?key=7de8697669288fc848e12a08f58d995e&s=rsv3&city=**市&address=**省**市**区**路23号
string uri = " http://restapi.amap.com/v3/geocode/geo";
string parameter = string.Format("key={0}&s={1}&city={2}&address={3}", "7de8697669288fc848e12a08f58d995e", "rsv3", "**(市名称)", address); // {"status":"1","info":"OK","infocode":"10000","count":"1","geocodes":[{"formatted_address":"***省**市**区***路|23号","province":"***","citycode":"***","city":"***市","district":"***区","township":[],"neighborhood":{"name":[],"type":[]},"building":{"name":[],"type":[]},"adcode":"330105","street":[],"number":[],"location":"120.151367,30.362293","level":"门牌号"}]}
string result = GetRequesetContext(uri, parameter);
if (string.IsNullOrEmpty(result) || result.IndexOf("location") == -)
{
lock (errorLocker)
{
writerError.WriteLine(result);
}
}
else
{
int indexCount = ;
List<string> lnglatItems = new List<string>();
foreach (string resultItem in result.Split(new string[] { "\",\"", ",\"" }, StringSplitOptions.RemoveEmptyEntries))
{
if (resultItem.IndexOf("location") != -)
{
indexCount++;
lnglatItems.Add(resultItem.Split(new char[] { ':' })[].Replace("\"", string.Empty));
}
}
if (indexCount == )
{
lock (resultLocker)
{
writerResult.WriteLine(address + "|" + lnglatItems[] + "|" + imsi);
}
}
else
{
lock (resultLocker)
{
writerError.WriteLine(address + "|" + string.Join(",", lnglatItems) + "|" + imsi);
}
}
}
}
catch (Exception ex)
{
logger.Error("{0}\r\n{1}", ex.Message, ex.StackTrace);
lock (errorLocker)
{
writerError.WriteLine(line);
}
}
finally
{
lock (maxThreadCountLcker)
{
if (Interlocked.Decrement(ref fakeMaxThreadCount) == )
{
finish.Set();
}
}
}
}

 备注:

关于ThreadPool线程池内最大线程控制函数:SetMaxThreads 设置可以同时处于活动状态的线程池的请求数目。 所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。

[SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
public static bool SetMaxThreads(
int workerThreads,
int completionPortThreads
)

workerThreads:线程池中辅助线程的最大数目。

completionPortThreads:线程池中异步 I/O 线程的最大数目。

但是,需要注意事项:

不能将辅助线程的数目或 I/O 完成线程的数目设置为小于计算机的处理器数目。

如果承载了公共语言运行时,例如由 Internet 信息服务 (IIS) 或 SQL Server 承载,主机可能会限制或禁止更改线程池大小。

更改线程池中的最大线程数时需谨慎。 虽然这类更改可能对您的代码有益,但对您使用的代码库可能会有不利的影响。

将线程池大小设置得太大可能导致性能问题。 如果同时执行的线程太多,任务切换开销就成为影响性能的一个主要因素。

c#:ThreadPool实现并行分析,并实现线程同步结束的更多相关文章

  1. 分析.Net里线程同步机制

    我 们知道并行编程模型两种:一种是基于消息式的,第二种是基于共享内存式的. 前段时间项目中遇到了第二种 使用多线程开发并行程序共享资源的问题 ,今天以实际案例出发对.net里的共享内存式的线程同步机制 ...

  2. Python并行编程(五):线程同步之信号量

    1.基本概念 信号量是由操作系统管理的一种抽象数据类型,用于在多线程中同步对共享资源的使用.本质上说,信号量是一个内部数据,用于标明当前的共享资源可以有多少并发读取. 同样在threading中,信号 ...

  3. Python并行编程(三):线程同步之Lock

    1.基础概念 当两个或以上对共享内存操作的并发线程中,如果有一个改变数据,又没有同步机制的条件下,就会产生竞争条件,可能会导致执行无效代码.bug等异常行为. 竞争条件最简单的解决方法是使用锁.锁的操 ...

  4. Python并行编程(七):线程同步之事件

    1.基本概念 事件是线程之间用于通讯的对象.有的线程等待信号,有的线程发出信号.基本上事件对象都会维护一个内部变量,可以通过set方法设置为true,也可以通过clear方法设置为false.wait ...

  5. Python并行编程(六):线程同步之条件

    1.基本概念 条件指的是应用程序状态的改变.其中某些线程在等待某一条件发生,其 他线程会在该条件发生的时候进行通知,一旦条件发生,线程会拿到共享资源的唯一权限. 2.示例代码 from threadi ...

  6. Python并行编程(四):线程同步之RLock

    1.基本概念 如果想让只有拿到锁的线程才能释放该锁,那么应该使用RLock()对象.当需要在类外面保证线程安全,又要在类内使用同样方法的时候RLock()就很使用. RLock叫做Reentrant ...

  7. C#并行编程-线程同步原语

    菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...

  8. Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) JAVA日志的前世今生 .NET MVC采用SignalR更新在线用户数 C#多线程编程系列(五)- 使用任务并行库 C#多线程编程系列(三)- 线程同步 C#多线程编程系列(二)- 线程基础 C#多线程编程系列(一)- 简介

    Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) 一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 一.前言 ...

  9. Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...

随机推荐

  1. 学习资料分享(Java第一行代码视频)<susmote.com>

    17年买了一本书,第一行代码(JAVA),李兴华编写的. 一开始我是按照书本一页一页的啃,一个点一个点的去学,虽然当时学的有些枯燥,但里面的知识点大部分还是弄的懂,只是一次偶然,因为有点质疑书上写的( ...

  2. 【原创】开启PowerShell远程管理

    非域网络,开启PowerShell远程管理,命令如下: 以下操作,PS命令窗口,必须都以管理员省份执行. Step 1: 机器A和B,分别开启PowerShell远程管理服务A = 192.168.3 ...

  3. 【Bootstrap】 bootstrap-table表格组件

    [Bootstrap-table] 顾名思义,这个组件专注于bootstrap风格的表格的设计,并且提供了很多表格的基础和进阶的功能,给我们开发前端的表格省下很多力气. 本文主要参考这位博主的系列文章 ...

  4. 【Darwin】 越狱后玩耍IPhone系统

    玩耍IOS系统 大家都知道IOS是自Mac OS修改而来的.而Mac OS和IOS的共同核心是Darwin,其基于FreeBSD发展而来,整体而言也是个类Unix系统.之前把自己的手机越狱之后正好开始 ...

  5. 源码实现 --> strdel

    删除字符串中某个字符strdel 函数 char *strDel(char* str,const char chToDel) 不是库里面的函数,自己实现的原型,删除str中所有的chToDel字符. ...

  6. 设计模式 --> (6)原型模式

    原型(Prototype)模式 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知 ...

  7. python全栈开发-Day10 装饰器(闭合函数的应用场)

    一. 装饰器 装饰器就是闭包函数的一种应用场景 什么是闭包函数?我们再来回忆一下: 闭包函数: 定义在函数内部的函数,并且该函数包含对外部函数作用域(强调:对全局作用域名字的引用不算闭包)名字的引用, ...

  8. mui手机图片压缩上传+C#

    前台参考网址:http://www.bcty365.com/content-146-3263-1.html <html> <head> <meta charset=&qu ...

  9. 云计算--网络原理与应用--20171120--VLAN与三层交换机配置

    什么是VLAN及其配置 Trunk的原理与配置 三层交换机的基本配置 实验:配置一个三层交换机 一 VLAN 的概念及优势 VLAN(virtual local area network)就是虚拟局域 ...

  10. 设置如何远程连接mysql数据库

    安装好mysql5.6.37后,默认情况下,只允许本地登录,禁止远程登录. 可以现在本地安装好连接工具,比如sqlyog或者navicat 登陆后,切换至mysql数据库 执行下面2条语句 '; FL ...