城市经纬度 json 理解SignalR Main(string[] args)之args传递的几种方式 串口编程之端口 多线程详细介绍 递归一个List<T>,可自己根据需要改造为通用型。 Sql 优化解决方案
城市经纬度 json
https://www.cnblogs.com/innershare/p/10723968.html
理解SignalR
ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现即时通信(即:客户端(Web页面)和服务器端可以互相实时的通知消息及调用方法),即时通讯Web服务就是服务器将内容自动推送到已经连接的客户端,而不是服务器等待客户端发起一个新的数据请求。
SignalR有三种传输模式:LongLooping(长轮询)、WebSocket(HTML5的WEB套接字)、Forever Frame(隐藏框架的长请求连接),可以在WEB客户端显式指定一种或几种,也可以采取默认(推荐),若采取默认,SignalR会根据浏览器的环境自动选择合适的传输方式。
SignalR能够被运用到各种类型的即时响应应用。你完全可以运用SignalR实现一个在线即时聊天应用。当然SignalR也运用在用户需要随时更新数据的网页运用,比如仪表盘和监控运用,其他协同应用程序(文档协同操作)、工作流更新或者是即时表格。
SignalR同样也被运用在一些需要高频繁从服务器更新的应用,比如实时在线游戏。
SignalR 提供了使用JavaScript API 方法创建服务器对客户端的远程过程调用协议(RPC)的,它能够让你从客户端浏览器(或者其他平台)从.NET服务端发起远程调用。SignalR 同样提供了管理远程连接和连接组的API方法。
SignalR 不仅能够自动管理连接,而且能够同时向所有的客户端广播消息,就像聊天室一样。你也能够发送消息到指定的客户端。SignalR提供的连接是持久的,它不像传统的HTTP连接需要为每次收发消息建立单独的连接。
SignalR 同时在服务端提供了远程过程调用协议(RPC),让你能够“主动”推送消息到浏览器中的客户端,而不像普通的Web服务一样的应答方式。
SignalR 应用能够运用到成千的客户端上,通过使用服务总线、SQL Server或者Redis。
SignalR 是开源的,能够通过GitHub很容易得到。
SignalR 和 WebSocket
你可以使用SignalR 使用最新的WebSocket 传输,同时也能够让你回退到原有的传输方式。你可以直接使用SignalR 使用 WebSocket,因为SignalR 已经替你封装好许多你需要实现的方法。最重要的是你使用SignalR不用担心为老的客户端实现WebSocket而采用两套不同的逻辑编码方式。使用SignalR 实现WebSocket你不用担心 WebSocket的更新而去修改代码,SignalR会在传输方式上使用WebSocket最新的传输方式,同时提供了一连串的接口能够让你来支持不同版本的客户端。
当你想创建一个纯粹的WebSocket解决方案的时候,SignalR 提供了所有的方法,比如回退到其他传输方式或者更新到WebSocket 的实现方法。
Websocket-Sharp获取客户端IP地址和端口号
Websocket-Sharp获取客户端IP地址和端口号
//OnOpen事件
protected override void OnOpen() { string IPAddress = base.Sessions.Sessions.First().Context.UserEndPoint.ToString();
string IPAddress2 = base.Context.UserEndPoint.ToString(); WebsocketMain.Account++; base.OnOpen(); }
在OnOpen事件内,通过
base.Sessions.Sessions.First().Context.UserEndPoint.ToString();
string IPAddress2 = base.Context.UserEndPoint.ToString();
函数以上两个函数都可以获取到客户端的IP地址及端口号
Main(string[] args)之args传递的几种方式
1、通过配置project属性传递。如图:
2、通过代码传递
3、通过命令行传递
串口编程之端口
软件程序与硬件通过串口交换数据,首先要知道COM口,但是COM口是由计算机动态随机分配的。也就是说COM口的编号是变化的。因此我们在编程时不能将COM的编号写死。针对此问题我们想到的可能是将所有COM口读出来绑定到下拉框,软件启动后从下拉框选择需要的端口。或者写到配置文件,软件启动前在配置文件配置。不用说这两种方法都不是很理想。后来通过查询资料原来COM口的变化会在注册表中记录。如图。
这样我们就有一个想法,能不能通过监控注册表来监测COM口。于是有了下面代码。
1 using Microsoft.Win32; 2 using System; 3 using System.Threading; 4 using System.Threading.Tasks; 5 6 namespace AutoTester.Utilities 7 { 8 public class COMListenService 9 { 10 /// <summary> 11 /// COM监听服务类 12 /// </summary> 13 /// <param name="serialPort">需要监听的端口</param> 14 //public COMListenService(string serialPort) 15 //{ 16 // _SerialPort = serialPort; 17 //} 18 19 /// <summary> 20 /// 监听连接状态事件 21 /// </summary> 22 public event Action<string, bool> ConnectionStatusChanged; 23 24 /// <summary> 25 /// 连接状态 26 /// </summary> 27 private bool _connectionStatus; 28 29 /// <summary> 30 /// 连接状态 31 /// </summary> 32 private bool ConnectionStatus 33 { 34 get { return _connectionStatus; } 35 set 36 { 37 if (_connectionStatus != value) 38 { 39 _connectionStatus = value; 40 ConnectionStatusChanged(_SerialPort, value); 41 } 42 } 43 } 44 45 46 /// <summary> 47 /// 开始监听 48 /// </summary> 49 public void StartListen() 50 { 51 _TokenSource = new CancellationTokenSource(); 52 53 Task.Factory.StartNew(() => 54 { 55 Listenning(); 56 }); 57 } 58 59 /// <summary> 60 /// 停止监听 61 /// </summary> 62 public void StopListen() 63 { 64 _TokenSource.Cancel(); 65 } 66 67 /// <summary> 68 /// 监听端口 69 /// </summary> 70 public void Listenning() 71 { 72 while (true) 73 { 74 Thread.Sleep(ListenFrequency); 75 76 if (_TokenSource.IsCancellationRequested) 77 { 78 break; 79 } 80 81 lock (_Locker) 82 { 83 bool isExist = false; 84 RegistryKey keyCom = Registry.LocalMachine.OpenSubKey(@"Hardware\DeviceMap\SerialComm"); 85 if (keyCom != null) 86 { 87 string[] sSubKeys = keyCom.GetValueNames(); 88 89 foreach (string sName in sSubKeys) 90 { 91 string sValue = string.Empty; 92 93 //if (sValue == _SerialPort) 94 //{ 95 // isExist = true; 96 // break; 97 //} 98 99 if (sName.Contains("ProlificSerial") || sName.ToUpper().Contains("VCP")) 100 { 101 _SerialPort = (string)keyCom.GetValue(sName); 102 isExist = true; 103 break; 104 } 105 } 106 } 107 108 ConnectionStatus = isExist; 109 } 110 } 111 } 112 113 114 private string _SerialPort; 115 private CancellationTokenSource _TokenSource; 116 private const int ListenFrequency = 1000; //监听频率(毫秒) 117 private static readonly object _Locker = new object(); 118 } 119 }
(注:ProlificSerial和VCP和你的硬件设备有关,插上你的硬件后观察注册表变化)
调用非常简单,直接上代码。
1 COMListenService cls = new COMListenService(); 2 cls.ConnectionStatusChanged += Cls_ConnectionStatusChanged; 3 cls.StartListen();
1 /// <summary> 2 /// 3 /// </summary> 4 /// <param name="comPort"></param> 5 /// <param name="status"></param> 6 private static void Cls_ConnectionStatusChanged(string comPort, bool status) 7 { 8 if (status) 9 { 10 //comPort已插入 11 } 12 else 13 { 14 //comPort已拔出 15 } 16 }
测试后,完美一切正常。
多线程详细介绍
什么是进程线程:我们来看一下自己的任务管理器
这里的每一项都是一个进程,我们的发布的每一个应用程序都需要一个进程去运行,在一个进程内可以有多个线程去计算执行程序。我们看下面的图片:
我们可以看一下进程和线程的数量,很明显可以看出,线程和进程的关系。我们的每一个操作都需要一个线程来执行,鼠标的点击就需要线程去响应我们的操作。
现在我们不难理解,我们一个应用程序就代表一个进程,想让我们的程序高效的运行我们就可以启用多个线程去执行了,当然采用多线程的话有好处也是有代价的,好处合理的利用计资源了,但是线程过多了,你的CPU利用率就加大了,也有可能导致电脑的卡死。
在我们的程序中线程的代表就是:Thread本篇文章我们就说一下线程。我们先了解一下同步异步。
同步:指的是在同一线程下执行,并且会等待结果执行完毕。
异步:不再同一个线程下执行,并且执行得顺序不可控,不会等待执行结果完毕。
我们先用委托来演示一下多线程,如果不怎么了解委托得可以看一下上一篇文章:
DelegateMethod Method = () => { Console.WriteLine($"我的线程ID是:{Thread.CurrentThread.ManagedThreadId}"); }; Method.BeginInvoke(null, null); Method.Invoke();
本来想用上面的代码去先简单的演示一下,谁知道NetCore 的程序集提供了,但是平台目前还不支持。 我们可以私下使用NET 试一下。
使用Thread 演示
Console.WriteLine($"*********************************我是同步方法 *******************************"); for (int i = 0; i < 10; i++) { Console.WriteLine($"我的线程ID是:{Thread.CurrentThread.ManagedThreadId}"); } Console.WriteLine($"*********************************同步方法结束 *******************************"); Console.WriteLine($"*********************************我是异步方法 *******************************"); for (int i = 0; i < 10; i++) { Thread thread = new Thread(() => { Console.WriteLine($"我的线程ID是:{Thread.CurrentThread.ManagedThreadId}"); }); thread.Start(); } Console.WriteLine($"*********************************异步方法结束 *******************************"); Console.ReadKey();
执行结果:
上面的代码执行结果中我们可以看到:同步方法 是同一个线程来执行的,之上而下有序的执行,但是异步方法启动了多个线程去执行的,并且线程是无序的。看到这样的情况我们就会知道如果我启动了很多线程线程用完之后也是有回收的,回收之后同样会分配的,也就是说同一个操作中线程的ID 可能会多次出现的。
Console.WriteLine($"*********************************异步启动线程数量计算 *******************************"); List<int> ListInt = new List<int>(); int Conut = 0; for (int i = 0; i < 2000; i++) { Thread thread = new Thread(() => { if (ListInt.Contains(Thread.CurrentThread.ManagedThreadId)) { Conut++; Console.WriteLine($"我的重复的线程我的 ID是:{Thread.CurrentThread.ManagedThreadId} 重复线程总数量:{Conut}"); } ListInt.Add(Thread.CurrentThread.ManagedThreadId); }); thread.Start(); } Console.WriteLine($"*********************************异步启动线程数量计算结果:{ListInt.Count} *******************************");
结果:
上面的结果我们可以看到:线程回收再利用。其实thread线程的回收就是我们的GC来做的,这就是C# 的强大之处,自动帮助我们回收了。需要注意的是这样使用线程我们的回收过程是比较慢的,这个回收速度是我们计算机性能决定的。
在上面的结果中我们可以看到我们总共申请了1996个线程,其中有881个线程是重复的,线程的申请和销毁是耗费很多性能的,接下来我们看一下线程池。
线程thread有哪些可操作的属性
CurrentContext |
获取线程正在其中执行的当前上下文。 |
CurrentCulture |
获取或设置当前线程的区域性。 |
CurrentPrinciple |
获取或设置线程的当前负责人(对基于角色的安全性而言)。 |
CurrentThread |
获取当前正在运行的线程。 |
CurrentUICulture |
获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源。 |
ExecutionContext |
获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。 |
IsAlive |
获取一个值,该值指示当前线程的执行状态。 |
IsBackground |
获取或设置一个值,该值指示某个线程是否为后台线程。 |
IsThreadPoolThread |
获取一个值,该值指示线程是否属于托管线程池。 |
ManagedThreadId |
获取当前托管线程的唯一标识符。 |
Name |
获取或设置线程的名称。 |
Priority |
获取或设置一个值,该值指示线程的调度优先级。 |
ThreadState |
获取一个值,该值包含当前线程的状态。 |
线程池:ThreadPool
ThreadPool:线程池中的线程都是后台线程IsBackground属性都是True.不会影响所有的前台线程,也就是说不会影响用户体验。每个线程都有默认的堆栈大小和优先级,位于多线程池中。 一旦线程池中的线程完成任务,它将返回到等待线程队列中,这时我们就可以利用这些闲置的线程。通过这种重复使用,应用程序可以避免产生为每个任务创建新线程的开销。在每一个进程中都只会有一个线程池
//public static bool SetMaxThreads(int workerThreads, int completionPortThreads); //public static bool SetMinThreads(int workerThreads, int completionPortThreads); //workerThreads 工作线程数量 completionPortThreads I/O线程数量 ThreadPool.SetMaxThreads(12,12); ThreadPool.SetMinThreads(12, 12);
我这里设置工作线程,I/O线程数量来源于我得计算机核心数量,保持每个核心最大最小线程都启动一个。查看计算机处理器核心数量。
上面的设置是说我在线程池中给准备了数量为12 的线程。你可以申请最多12个线程,在使用完之后我会立马进行自动的回收,回收之后的线程继续存放在线程池中等待使用。相比于 Thread线程池ThreadPool对于线程的回收更快,性能更好。
代码看一下性能:
Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < 1000; i++) { Thread thread = new Thread(() => { Console.WriteLine($"*********************************我是多线程Thread方法 *******************************"); }); thread.Start(); } Console.WriteLine($"*********************************Thread方法结束 耗费时间 :{stopwatch.ElapsedMilliseconds} *******************************"); Console.WriteLine($"*********************************多线程ThreadPool启动 *******************************"); Stopwatch stopwatch1 = new Stopwatch(); stopwatch1.Start(); for (int i = 0; i < 1000; i++) { WaitCallback act = (t) => { Console.WriteLine($"*********************************我是多线程ThreadPool方法 *******************************"); }; ThreadPool.QueueUserWorkItem(act); } Console.WriteLine($"*********************************ThreadPool方法结束 耗费时间 :{stopwatch1.ElapsedMilliseconds} *******************************");
结果:Thread
结果:ThreadPool
上面的两个方法我们都只是输出一行字符,但是以1000次的来说看一下性能相差有多少。所以建议大家都使用线程池。
推荐官方文档:https://docs.microsoft.com/zh-cn/dotnet/standard/threading/?view=netframework-4.7.2
线程池:Task
随着框架的发展我们有了Task 他也是基于ThreadPool来重新封装的。他的出现方便了我们对多线程的回调等待更好的操作。
推荐一篇博客:https://www.cnblogs.com/lonelyxmas/p/9509298.html
并行计算的多线程 Parallel
Parallel 多线程,这个类似同步的,他是在Task的基础之上又一次的封装。假如说我们启动多个线程,她像其他的一样启动了很多的子线程去执行的,而是和当前线程一样并行去执行的。并且当前线程也参与执行。他会卡住线程,等到全部执行完毕后才会继续
这个操作上不如Task 灵活,比如Task 可以等待其中一个线程执行完成后继续主线程,Parallel 是必须等待全部执行完毕。
Parallel里面大致分为三个方法: For,ForEach,Invoke
Invoke:
Console.WriteLine($"*********************************我是主线程线程 ID:{Thread.CurrentThread.ManagedThreadId} *******************************"); Action act = () => { Console.WriteLine($"我的线程ID是:{Thread.CurrentThread.ManagedThreadId}"); }; Parallel.Invoke(act, act, act, act, act);
结果:
上面结果中我们可以看到主线程参与了进来。
ForEach:
List<int> vs = new List<int>() { 1, 2, 3, 4, 5 }; Parallel.ForEach<int>(vs, t => { Console.WriteLine($"*********************************我是 {t} *******************************"); });
结果:
For
List<int> vs = new List<int>() { 1, 2, 3, 4, 5 }; //从零开始 循环多少次 Parallel.For(0, vs.Count,t=> { Console.WriteLine($"*********************************我是 {t} *******************************"); });
结果:
上面的代码中我们可以看到Parallel 适合在我们循环的时候去使用这样并行的去执行,我们可以减少程序的执行时间。
如果当我们需要执行的集合过大有可能会 并行很多线程时我们怕会影响我们计算机的I/O 我们还可以设置最大的并行数防止程序执行时i/o风暴
//设置 Parallel 最大并行线程的数量 ParallelOptions options = new ParallelOptions(); //最大并行数为10; options.MaxDegreeOfParallelism = 10;
Parallel 官方介绍:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.parallel?redirectedfrom=MSDN&view=netframework-4.7.2
获取当前计算机 最大线程数
int workerThreads; int completionPortThreads; ThreadPool.GetMaxThreads( out workerThreads, out completionPortThreads); Console.WriteLine($"最大工作线程:{workerThreads} 最大I/O线程:{completionPortThreads} "); ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads); Console.WriteLine($"最小工作线程:{workerThreads} 最小 I/O线程:{completionPortThreads} ");
结果:
有不足之处 希望大家指出相互学习,
转载请注明出处 谢谢!
递归一个List<T>,可自己根据需要改造为通用型。
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
|
/// <summary> /// 递归一个List<ProvinceOrg> /// </summary> /// <returns></returns> public void RecursionProvinceOrgs(List<ProvinceOrg> provinceOrgs, ref List<ProvinceOrg> _provinceOrgs) { if (provinceOrgs != null && provinceOrgs.Count() > 0) { // 1、通过linq查询出children为空的。 var _provinceOrgs_ = provinceOrgs.Where(p => p.children.Count() == 0); if (_provinceOrgs_.Count() > 0) { //2、将children为空的添加至_provinceOrgs _provinceOrgs.AddRange(_provinceOrgs_); } // 处理剩余不为空的,父级的ORG var _provinceOrgs_Surplus = provinceOrgs.Where(p => p.children.Count() > 0); foreach (ProvinceOrg provinceOrg in _provinceOrgs_Surplus) { ProvinceOrg provinceOrgClone = provinceOrg.Clone(); _provinceOrgs.Add(provinceOrgClone); RecursionProvinceOrgs(provinceOrg.children, ref _provinceOrgs); } } } |
实体类:
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
38
|
public class ProvinceOrg : ICloneable { public int orgId { get ; set ; } public int parentOrgId { get ; set ; } public int areaId { get ; set ; } public string areaCode { get ; set ; } public string orgName { get ; set ; } public string fullOrgName { get ; set ; } public string orgType { get ; set ; } public string state { get ; set ; } public string orgCode { get ; set ; } public int seq { get ; set ; } public string isBusDep { get ; set ; } public string depCateCode { get ; set ; } public string legalPerson { get ; set ; } public string contacts { get ; set ; } public string phone { get ; set ; } public string address { get ; set ; } public string orgFunctions { get ; set ; } public int num; public List<ProvinceOrg> children { get ; set ; } /// <summary> /// 克隆并返回一个对象。 /// </summary> /// <returns></returns> public ProvinceOrg Clone() { ProvinceOrg p = (ProvinceOrg) this .MemberwiseClone(); p.children = new List<ProvinceOrg>(); return p; } object ICloneable.Clone() { return this .Clone(); } } |
Sql 优化解决方案
转自:https://blog.csdn.net/jie_liang/article/details/77340905
用以记录:
在sql查询中为了提高查询效率,我们常常会采取一些措施对查询语句进行sql优化,下面总结的一些方法,有需要的可以参考参考。
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
5.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.下面的查询也将导致全表扫描:
select id from t where name like '%abc%'
7.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
8.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)='abc'--name以abc开头的id
应改为:
select id from t where name like 'abc%'
9.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
10.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,
否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
11.不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(...)
12.很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
13.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,
如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
14.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,
因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。
一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
15.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。
这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
16.尽可能的使用 varchar 代替 char ,因为首先变长字段存储空间小,可以节省存储空间,
其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
17.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
18.避免频繁创建和删除临时表,以减少系统表资源的消耗。
19.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
20.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,
以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
21.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
22.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
23.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
24.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。
在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
25.尽量避免大事务操作,提高系统并发能力。26.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。