C# 线程池ThreadPool

什么是线程池?为什么要用线程池?怎么用线程池?

1. 什么是线程池?

        .NET Framework的ThreadPool类提供一个线程池,该线程池可用于执行任务、发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。那么什么是线程池?线程池其实就是一个存放线程对象的“池子(pool)”,他提供了一些基本方法,如:设置pool中最小/最大线程数量、把要执行的方法排入队列等等。ThreadPool是一个静态类,因此可以直接使用,不用创建对象。

2. 为什么要用线程池?好处是什么?

        微软官网说法如下:许多应用程序创建大量处于睡眠状态,等待事件发生的线程。还有许多线程可能会进入休眠状态,这些线程只是为了定期唤醒以轮询更改或更新的状态信息。 线程池,使您可以通过由系统管理的工作线程池来更有效地使用线程。

        说得简单一点,每新建一个线程都需要占用内存空间和其他资源,而新建了那么多线程,有很多在休眠,或者在等待资源释放;又有许多线程只是周期性的做一些小工作,如刷新数据等等,太浪费了,划不来,实际编程中大量线程突发,然后在短时间内结束的情况很少见。于是,就提出了线程池的概念。线程池中的线程执行完指定的方法后并不会自动消除,而是以挂起状态返回线程池,如果应用程序再次向线程池发出请求,那么处以挂起状态的线程就会被激活并执行任务,而不会创建新线程,这就节约了很多开销。只有当线程数达到最大线程数量,系统才会自动销毁线程。因此,使用线程池可以避免大量的创建和销毁的开支,具有更好的性能和稳定性,其次,开发人员把线程交给系统管理,可以集中精力处理其他任务。


3. 怎么使用线程池?

其实线程池使用起来很简单,如下

a.设置线程池最大最小:

ThreadPool.SetMaxThreads (int workerThreads,int completionPortThreads)
设置可以同时处于活动状态的线程池的请求数目。所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。还可以设置最小线程数。

b.将任务添加进线程池:

ThreadPool.QueueUserWorkItem(new WaitCallback(方法名));


ThreadPool.QueueUserWorkItem(new WaitCallback(方法名), 参数);

举个小例子,线程池中最多5个线程,执行一个方法60次,算5年总工资,如下:



如果不采用线程池,恐怕要开60线程异步执行Run()方法,空间资源之浪费,可见一斑。而现在我们最多用了5个线程,1秒内即可执行完毕,效率、性能都很好。

----------------------------------------------------------------------------------------------------------------------------------

C#线程池ThreadPool.QueueUserWorkItem接收线程执行的方法返回值

  最近在项目中需要用到多线程,考虑了一番,选择了ThreadPool,我的需求是要拿到线程执行方法的返回值,

但是ThreadPool.QueueUserWorkItem的回调方法默认是没有返回值的,搜了搜,都是简单介绍ThreadPool.QueueUserWorkItem的各种

用法,只能自己想办法了。

  回调方法不带返回值,迂回一下,回调方法用对象的方法,返回值放在对象的属性中,在对象方法执行时将需要的返回值赋值给对应属性。

等所有线程执行完,循环对象列表,取回返回值,然后想怎么处理返回值就OK了。上代码: 

  封装对象:

复制代码
 1 using System;
 2 using System.Threading;
 3  public class ThreadReturnData
 4     {
 5         public ManualResetEvent manual;
 6         public string res;
 7 
 8         public void ReturnThreadData(object obj)
 9         {
10             //线程耗时操作方法
11             res = DoSomething(obj);
12              manual.Set();
13         }
14     }
复制代码

  多线程调用:

复制代码
 1  List<ThreadReturnData> testList = new List<ThreadReturnData>();
 2 IList<ManualResetEvent> arrManual = new List<ManualResetEvent>();
 3 for (int i = 0; i < i; i++)
 4 {
 5                             ThreadReturnData temp = new ThreadReturnData();
 6                             temp.manual = new ManualResetEvent(false);
 7                             arrManual.Add(temp.manual);
 8                             ThreadPool.QueueUserWorkItem(new WaitCallback(temp.ReturnThreadData), i);
 9                             testList.Add(temp);
10                         }
11 }
12                     if (arrManual.Count > 0)
13                     {
14                         ////等待所有线程执行完
15                         WaitHandle.WaitAll(arrManual.ToArray());
16                     }
17  foreach (ThreadReturnData d in testList)       
18  {
19 d.res;
20 //todo
21 }
复制代码
-------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------------------------

https://blog.csdn.net/zhaoguanghui2012/article/details/52910035

一、CLR线程池

  管理线程开销最好的方式:

  1. 尽量少的创建线程并且能将线程反复利用(线程池初始化时没有线程,有程序请求线程则创建线程);
  2. 最好不要销毁而是挂起线程达到避免性能损失(线程池创建的线程完成任务后以挂起状态回到线程池中,等待下次请求);
  3. 通过一个技术达到让应用程序一个个执行工作,类似于一个队列(多个应用程序请求线程池,线程池会将各个应用程序排队处理);
  4. 如果某一线程长时间挂起而不工作的话,需要彻底销毁并且释放资源(线程池自动监控长时间不工作的线程,自动销毁);
  5. 如果线程不够用的话能够创建线程,并且用户可以自己定制最大线程创建的数量(当队列过长,线程池里的线程不够用时,线程池不会坐视不理);

  微软早就替我们想到了,为我们实现了线程池。

  CLR线程池并不会在CLR初始化时立即建立线程,而是在应用程序要创建线程来运行任务时,线程池才初始化一个线程。

  线程池初始化时是没有线程的,线程池里的。线程的初始化与其他线程一样,但是在完成任务以后,该线程不会自行销毁,而是以挂起的状态返回到线程池。直到应用程序再次向线程池发出请求时,线程池里挂起的线程就会再度激活执行任务。

  这样既节省了建立线程所造成的性能损耗,也可以让多个任务反复重用同一线程,从而在应用程序生存期内节约大量开销。

通过CLR线程池所建立的线程总是默认为后台线程,优先级数为ThreadPriority.Normal

二、工作者线程与I/O线程

  CLR线程池分为工作者线程(workerThreads)与I/O线程(completionPortThreads)两种:

  • 工作者线程是主要用作管理CLR内部对象的运作,通常用于计算密集的任务。
  • I/O(Input/Output)线程主要用于与外部系统交互信息,如输入输出,CPU仅需在任务开始的时候,将任务的参数传递给设备,然后启动硬件设备即可。等任务完成的时候,CPU收到一个通知,一般来说是一个硬件的中断信号,此时CPU继续后继的处理工作。在处理过程中,CPU是不必完全参与处理过程的,如果正在运行的线程不交出CPU的控制权,那么线程也只能处于等待状态,即使操作系统将当前的CPU调度给其他线程,此时线程所占用的空间还是被占用,而并没有CPU处理这个线程,可能出现线程资源浪费的问题。如果这是一个网络服务程序,每一个网络连接都使用一个线程管理,可能出现大量线程都在等待网络通信,随着网络连接的不断增加,处于等待状态的线程将会很消耗尽所有的内存资源。可以考虑使用线程池解决这个问题。

  线程池的最大值一般默认为1000、2000。当大于此数目的请求时,将保持排队状态,直到线程池里有线程可用。

  使用CLR线程池的工作者线程一般有两种方式:

  • 通过ThreadPool.QueueUserWorkItem()方法;
  • 通过委托;

  要注意,不论是通过ThreadPool.QueueUserWorkItem()还是委托,调用的都是线程池里的线程。

三、ThreadPool类常用方法

  通过以下两个方法可以读取和设置CLR线程池中工作者线程与I/O线程的最大线程数。

  1. ThreadPool.GetMax(out in workerThreads,out int completionPortThreads);
  2. ThreadPool.SetMax(int workerThreads,int completionPortThreads);

  若想测试线程池中有多少线程正在投入使用,可以通过ThreadPool.GetAvailableThreads(out in workThreads,out int conoletionPortThreads)方法。

方法说明
GetAvailableThreads剩余空闲线程数
GetMaxThreads最多可用线程数,所有大于此数目的请求将保持排队状态,直到线程池线程变为可用
GetMinThreads检索线程池在新请求预测中维护的空闲线程数。
QueueUserWorkItem启动线程池里得一个线程(队列的方式,如线程池暂时没空闲线程,则进入队列排队)
SetMaxThreads设置线程池中的最大线程数
SetMinThreads设置线程池最少需要保留的线程数
复制代码
    class Program{static void Main(string[] args){int i = 0;int j = 0;//前面是辅助(也就是所谓的工作者)线程,后面是I/O线程ThreadPool.GetMaxThreads(out i, out j);Console.WriteLine(i.ToString() + "   " + j.ToString()); //默认都是1000//获取空闲线程,由于现在没有使用异步线程,所以为空ThreadPool.GetAvailableThreads(out i, out j);Console.WriteLine(i.ToString() + "   " + j.ToString()); //默认都是1000
Console.ReadKey();}}
复制代码

四、各种调用线程池线程的方法

  1、通过QueueUserWorkItem启动工作者线程

  ThreadPool线程池中有两个重载的静态方法可以直接启动工作者线程:

  •   ThreadPool.QueueUserWorkItem(waitCallback);
  •   ThreadPool.QueueUserWorkItem(waitCallback,Object);

  先把WaitCallback委托指向一个带有Object参数的无返回值方法,再使用ThreadPool.QueueUserWorkItem(WaitCallback)就可以一步启动此方法,此时异步方法的参数被视为null。

  下面来试下用QueueUserWorkItem启动线程池里的一个线程。注意哦,由于是一直存在于线程池,所以不用new Thread()。

复制代码
    class Program{static void Main(string[] args){//工作者线程最大数目,I/O线程的最大数目ThreadPool.SetMaxThreads(1000, 1000);   //启动工作者线程ThreadPool.QueueUserWorkItem(new WaitCallback(RunWorkerThread));Console.ReadKey();}static void RunWorkerThread(object state){Console.WriteLine("RunWorkerThread开始工作");Console.WriteLine("工作者线程启动成功!");}}
复制代码

  输出:

  

  使用第二个重载方法ThreadPool.QueueUserWorkItem(WaitCallback,object)方法可以把object对象作为参数传送到回调函数中。

复制代码
    class Program{static void Main(string[] args){Person p = new Person(1,"刘备");//启动工作者线程ThreadPool.QueueUserWorkItem(new WaitCallback(RunWorkerThread), p);Console.ReadKey();}static void RunWorkerThread(object obj){Thread.Sleep(200);Console.WriteLine("线程池线程开始!");Person p = obj as Person;Console.WriteLine(p.Name);}}public class Person{public Person(int id,string name) { Id = id; Name = name; }public int Id { get; set; }public string Name { get; set; }}
复制代码

  输出结果如下:

  

  通过ThreadPool.QueueUserWork启动工作者线程非常方便,但是WaitCallback委托指向的必须是一个带有object参数的无返回值方法。所以这个方法启动的工作者线程仅仅适合于带单个参数和无返回值的情况。

  那么如果要传递多个参数和要有返回值又应该怎么办呢?那就只有通过委托了。

  2、BeginInvoke与EndInvoke委托异步调用线程

  异步调用委托的步骤如下:

  1. 建立一个委托对象,通过IAsyncResult BeginInvoke(string name,AsyncCallback callback,object state)异步调用委托方法,BeginInvoke方法除最后的两个参数外,其他参数都是与方法参数相对应的。
  2. 利用EndInvoke(IAsyncResult--上一步BeginInvoke返回的对象)方法就可以结束异步操作,获取委托的运行结果。
复制代码
    class Program{//除了最后两个参数,前面的都是你可定义的delegate string MyDelegate(string name,int age);static void Main(string[] args){//建立委托MyDelegate myDelegate = new MyDelegate(GetString);//异步调用委托,除最后两个参数外,前面的参数都可以传进去IAsyncResult result = myDelegate.BeginInvoke("刘备",22, null, null);  //IAsynResult还能轮询判断,功能不弱Console.WriteLine("主线程继续工作!");//调用EndInvoke(IAsyncResult)获取运行结果,一旦调用了EndInvoke,即使结果还没来得及返回,主线程也阻塞等待了//注意获取返回值的方式string data = myDelegate.EndInvoke(result);Console.WriteLine(data);Console.ReadKey();}static string GetString(string name, int age){Console.WriteLine("我是不是线程池线程" + Thread.CurrentThread.IsThreadPoolThread);Thread.Sleep(2000);return string.Format("我是{0},今年{1}岁!",name,age);}}
复制代码

  输出如下:

  

  这种方法有一个缺点,就是不知道异步操作什么时候执行完,什么时候开始调用EndInvoke,因为一旦EndInvoke主线程就会处于阻塞等待状态。

  3、IAsyncResult轮询

  为了克服上面提到的缺点,此时可以好好利用IAsyncResult提高主线程的工作性能,IAsyncResult有如下成员。

复制代码
public interface IAsyncResult
{object AsyncState {get;}       //获取用户定义的对象,它限定或包含关于异步操作的信息。WailHandle AsyncWaitHandle {get;}  //获取用于等待异步操作完成的 WaitHandle。bool CompletedSynchronously {get;} //获取异步操作是否同步完成的指示。bool IsCompleted {get;}        //获取异步操作是否已完成的指示。
}
复制代码

  示例如下:

复制代码
    class Program{delegate string MyDelegate(string name,int age);static void Main(string[] args){MyDelegate myDelegate = new MyDelegate(GetString);IAsyncResult result = myDelegate.BeginInvoke("刘备",22, null, null);Console.WriteLine("主线程继续工作!");//比上个例子,只是利用多了一个IsCompleted属性,来判断异步线程是否完成 while (!result.IsCompleted){Thread.Sleep(500);          Console.WriteLine("异步线程还没完成,主线程干其他事!");}string data = myDelegate.EndInvoke(result);Console.WriteLine(data);Console.ReadKey();}static string GetString(string name, int age){Thread.Sleep(2000);return string.Format("我是{0},今年{1}岁!",name,age);}}
复制代码

  输出如下:

  

  以上例子,除了IsCompleted属性外,还可以使用AsyncWaitHandle如下3个方法实现同样轮询判断效果:

  • WaitOne:判断单个异步线程是否完成;
  • WaitAny:判断是否异步线程是否有指定数量个已完成;
  • WaitAll:判断是否所有的异步线程已完成;

  WaitOne:

  //比上个例子,判断条件由IsCompleted属性换成了AsyncWaitHandle,仅此而已while (!result.AsyncWaitHandle.WaitOne(200)){Console.WriteLine("异步线程没完,主线程继续干活!");}

  WaitAny:

  //是否完成了指定数量WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle };while (WaitHandle.WaitAny(waitHandleList, 200) > 0){Console.WriteLine("异步线程完成数未大于0,主线程继续甘其他事!");}

  WaitAll:

  WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle };//是否全部异步线程完成while (!WaitHandle.WaitAll(waitHandleList, 200)){Console.WriteLine("异步线程未全部完成,主线程继续干其他事!");}

  4、IAsyncResult回调函数

  使用轮询方式来检测异步方法的状态非常麻烦,而且影响了主线程,效率不高。能不能异步线程完成了就直接调用实现定义好的处理函数呢?

  有,还是强大的IAsyncResult对象。

复制代码
    class Program{delegate string MyDelegate(string name, int age);static void Main(string[] args){//建立委托MyDelegate myDelegate = new MyDelegate(GetString);//倒数第二个参数,委托中绑定了完成后的回调方法IAsyncResult result1 = myDelegate.BeginInvoke("刘备",23, new AsyncCallback(Completed), null);//主线程可以继续工作而不需要等待Console.WriteLine("我是主线程,我干我的活,不再理你!");Thread.Sleep(5000);//Console.ReadKey();
        }static string GetString(string name, int age){Thread.CurrentThread.Name = "异步线程";//注意,如果不设置为前台线程,则主线程完成后就直接卸载程序了//Thread.CurrentThread.IsBackground = false;Thread.Sleep(2000);return string.Format("我是{0},今年{1}岁!", name, age);}//供异步线程完成回调的方法static void Completed(IAsyncResult result){//获取委托对象,调用EndInvoke方法获取运行结果AsyncResult _result = (AsyncResult)result;MyDelegate myDelegaate = (MyDelegate)_result.AsyncDelegate;//获得参数string data = myDelegaate.EndInvoke(_result);Console.WriteLine(data);//异步线程执行完毕Console.WriteLine("异步线程完成咯!");Console.WriteLine("回调函数也是由" + Thread.CurrentThread.Name + "调用的!");}}
复制代码

  输出如下:

  

  注意:

  1. 回调函数依然是在辅助线程中执行的,这样就不会影响主线程的运行。
  2. 线程池的线程默认是后台线程。但是如果主线程比辅助线程优先完成,那么程序已经卸载,回调函数未必会执行。如果不希望丢失回调函数中的操作,要么把异步线程设为前台线程,要么确保主线程将比辅助线程迟完成。

  到目前为止,BeginInvoke("刘备",23, new AsyncCallback(Completed), null)还有最后一个参数没用过的。那么最后一个参数是用来干什么?传参:

复制代码
namespace 控制台___学习测试
{class Program{delegate string MyDelegate(string name, int age);static void Main(string[] args){Person p = new Person(2,"关羽");//建立委托MyDelegate myDelegate = new MyDelegate(GetString);//最后一个参数的作用,原来是用来传参的IAsyncResult result1 = myDelegate.BeginInvoke("刘备", 23, new AsyncCallback(Completed), p);//主线程可以继续工作而不需要等待Console.WriteLine("我是主线程,我干我的活,不再理你!");Console.ReadKey();}static string GetString(string name, int age){Thread.CurrentThread.Name = "异步线程";//注意,如果不设置为前台线程,则主线程完成后就直接卸载程序了Thread.CurrentThread.IsBackground = false;Thread.Sleep(2000);return string.Format("我是{0},今年{1}岁!", name, age);}//供异步线程完成回调的方法static void Completed(IAsyncResult result){//获取委托对象,调用EndInvoke方法获取运行结果AsyncResult _result = (AsyncResult)result;MyDelegate myDelegaate = (MyDelegate)_result.AsyncDelegate;//获得参数string data = myDelegaate.EndInvoke(_result);Console.WriteLine(data);Person p = result.AsyncState as Person;Console.WriteLine("传过来的参数是:" + p.Name);//异步线程执行完毕Console.WriteLine("异步线程完成咯!");Console.WriteLine("回调函数也是由" + Thread.CurrentThread.Name + "调用的!");}}public class Person{public Person(int id, string name){Id = id;Name = name;}public int Id{get;set;}public string Name{get;set;}}
}
复制代码

  输出如下:

  


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458460.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

苹果应用上架,图片的要求(2017年4月27日)

看这个提示应该就明白了吧。 哈哈&#xff0c;我还是自己再说一遍加深一下印象吧&#xff1a;如果应用在各个尺寸iphone屏幕上面外观一样&#xff0c;就只准备5.5英寸的图就可以了&#xff1b;如果有所不同&#xff0c;就按照实际情况&#xff0c;准备不同屏幕尺寸的图片即可。…

android Instrumentation 转载

Android提供了一系列强大的测试工具&#xff0c;它针对Android的环境&#xff0c;扩展了业内标准的JUnit测试框架。尽管你可以使用JUnit测试Android工程&#xff0c;但Android工具允许你为应用程序的各个方面进行更为复杂的测试&#xff0c;包括单元层面及框架层面。Android测试…

【C#学习笔记】使用C#中的Dispatcher

form:https://www.jianshu.com/p/0714fc755988之前的文章说过了如何使用BackgroundWorker&#xff0c;今天要说的是WPF程序员处理多线程的另外一个方式 - Dispatcher当我们打开一个WPF应用程序即开启了一个进程&#xff0c;该进程中至少包含两个线程。一个线程用于处理呈现&…

html body标签

table table 属性&#xff1a; border &#xff1a;定义表格的边框宽度&#xff0c;默认为0&#xff0c;即无边框。<table border"1"> title &#xff1a;表格的提示信息&#xff0c;当鼠标移到表格上方时&#xff0c;所提示的信息。 cellpadding &#xff1a;…

DataReceivedEventHandler 委托 接收调用执行进程返回数据

https://msdn.microsoft.com/zh-cn/library/azure/system.diagnostics.datareceivedeventhandler备注创建 DataReceivedEventHandler 委托时&#xff0c;需要标识将处理该事件的方法。 若要将事件与事件处理程序关联&#xff0c;请将该委托的一个实例添加到事件中。 除非移除了…

coreData

CoreData使用 创建步骤流程 第一步先创建.xcdatamodeld文件&#xff08;New File -> iOS -> Core Data ->Data Model&#xff09; 屏幕快照 2016-07-07 下午10.40.16.png名字虽然可以任意取&#xff0c;但最好还是取和自己存储数据库名字一样的名字。这样可读性更高些…

PetaPoco初体验(转)

PetaPoco初体验&#xff08;转&#xff09; PetaPoco初体验&#xff08;转&#xff09;大部分转自&#xff1a; http://landyer.com/archives/138 PetaPoco C#微型ORM框架&#xff0c;基本无需配置&#xff0c;仅由单个cs文件构成&#xff0c;支持.net3.5 .net4.0。 截稿时Peta…

iOS当中的设计模式

代理模式 应用场景&#xff1a;当一个类的某些功能需要由别的类来实现&#xff0c;但是又不确定具体会是哪个类实现。 优势&#xff1a;解耦合 敏捷原则&#xff1a;开放-封闭原则 实例&#xff1a;tableview的 数据源delegate&#xff0c;通过和protocol的配合&#xff0c…

c#的dllimport使用方法详解

DllImport是System.Runtime.InteropServices命名空间下的一个属性类&#xff0c;其功能是提供从非托管DLL&#xff08;托管/非托管是微软的.net framework中特有的概念&#xff0c;其中&#xff0c;非托管代码也叫本地&#xff08;native&#xff09;代码。与Java中的机制类似&…

VS2010中 C++创建DLL图解

一、DLL的创建 创建项目: Win32->Win32项目&#xff0c;名称&#xff1a;MyDLL 选择DLL (D) ->完成. 1、新建头文件testdll.htestdll.h代码如下&#xff1a;#ifndef TestDll_H_#define TestDll_H_#ifdef MYLIBDLL#define MYLIBDLL extern "C" _declspec(dllimp…

使用公用表表达式的递归查询

微软从SQL2005起引入了CTE(Common Table Expression)以强化T-SQL。公用表表达式 (CTE) 具有一个重要的长处&#xff0c;那就是可以引用其自身。从而创建递归 CTE。递归 CTE 是一个反复运行初始 CTE 以返回数据子集直到获取完整结果集的公用表表达式。 当某个查询引用递归 CTE 时…

C#委托实现C++ Dll中的回调函数

from:https://blog.csdn.net/ferrycooper/article/details/63261771很多的Dll都是C和C写的&#xff0c;那么如果C#想要调用Dll中的函数怎么办&#xff0c;尤其是Dll函数其中一个参数是函数指针的&#xff0c;即里面有回掉函数的用C#怎么实现&#xff1f; C中的回掉函数在C#中有…

15个最好的HTML5前端响应式框架(2014)

文中的多个框架基于SASS创建&#xff0c;SCSS是一种比LESS更简洁的样式表编程语言&#xff0c;它能够编绎成CSS&#xff0c;可复用CSS代码&#xff0c;声明变量&#xff0c;甚至是函数&#xff0c;类Ruby/Python的语法。參见&#xff1a; LESS vs SASS&#xff1f;选择哪种CSS样…

【转载】Direct3D基础知识

原文&#xff1a;Direct3D基础知识 重新从头开始学习DX,以前太急于求成了,很多基础知识都没掌握就开始写程序了,结果出了问题很难解决.1. D3D体系结构D3D与GDI处与同一层次,区别在于,D3D可以使用HAL(Hardware Abstraction Layer)通过DDI来访问图形硬件,充分发挥硬件性能.…

关于Xcode隐藏打印的logs的方法

https://www.cnblogs.com/jukaiit/p/5881062.html 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 添加参数&#xff1a; Name &#xff1a;OS_ACTIVITY_MODE Value : disable

指针函数与函数指针的区别

首先它们之间的定义&#xff1a;1、指针函数是指带指针的函数&#xff0c;即本质是一个函数&#xff0c;函数返回类型是某一类型的指针。 类型标识符 *函数名(参数表)int *f(x&#xff0c;y);首先它是一个函数&#xff0c;只不过这个函数的返回值是一个地址值。函数返回值必须用…

C++走向远洋——63(项目二2、两个成员的类模板)

*/ * Copyright (c) 2016&#xff0c;烟台大学计算机与控制工程学院 * All rights reserved. * 文件名&#xff1a;text.cpp * 作者&#xff1a;常轩 * 微信公众号&#xff1a;Worldhello * 完成日期&#xff1a;2016年6月4日 * 版本号&#xff1a;V1.0 * 问题描述&…

iOS 抓包工具 charles工具

在Charles官网下载最新的 安装包 在电脑上安装完成之后&#xff0c;以 注册码 Registered Name: https://zhile.io License Key: 48891cf209c6d32bf4 进行注册即可完成 在手机上面设置代理&#xff1a;输入电脑的网络IP以及端口号 以下为查找的步骤&#xff1a; 在手机上手…

写一个Android输入法01——最简步骤

本文演示用Android Studio写一个最简单的输入法。界面和交互都很简陋&#xff0c;只为剔肉留骨&#xff0c;彰显写一个Android输入法的要点。 1、打开Android Studio创建项目&#xff0c;该项目和普通APP的不同之处在于它不需要添加任何Activity&#xff1a;我给该输入法命名为…

谈谈自己对于Auth2.0的见解

Auth的原理网上有很多&#xff0c;我这里就不在赘述了。 这里有张时序图我个人觉得是比较合理而且直观的&#xff0c;&#xff08;感谢这篇博文&#xff1a;http://justcoding.iteye.com/blog/1950270&#xff09; 参照这个流程&#xff0c;模拟了下部分代码&#xff0c;当然是…