多线程概述:
线程,是操作系统中的术语,是操作系统进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以有很多线程,每条线程并行执行不同的任务。同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。我们把用来执行用户任务的线程称为工作线程。而线程池,是一种成熟的线程使用模式。
为什么要创建线程池?
线程池属于对象池.所有对象池都具有一个非常重要的共性,就是为了最大程度复用对象.那么,线程池的最重要的特征也就是最大程度利用线程。所以线程池的目的就是为了减少创建和切换线程的额外开销,利用已经的线程多次循环执行多个任务从而提高系统的处理能力。
在ASP.NET工作进程中有两种线程池,Worker线程池处理所有传入的请求, I / O线程池处理的I / O(访问文件系统,Web服务和数据库等)。每个应用程序域都有其自己的线程池,可以排队到线程池的操作的数量只受可用内存的限制,然而,对线程池中的线程数的限制在这个过程中可以同时被激活。
当我们发出一个(异步)页面请求。一个Worker线程就会被从Worker线程池中取出, 这个Worker线程会触发I/O操作。当I/O操作开始时,另一个线程将会被从I/O线程池中取出,在收到I/O线程的返回值之前,Worker线程会一直处于闲置状态。所以,如果你的页面加载事件触发了多个I/O操作,那么,Worker线程就很可能会被闲置很长时间。而线程池能够把正处于闲置状态的Worker线程回收,使他能够为其他的页面请求提供服务。从而,降低系统开销。
演示demo(创建控制台项目):
测试编码:
代码语言:javascript复制using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace duo_0926
{
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(() => {
Console.WriteLine("启动吃药的监听:输入1代表吃药");
Ation1();
});
t1.Start();
Thread t2 = new Thread(() => {
Console.WriteLine("启动攻击的监听:输入2代表攻击");
Ation2();
});
t2.Start();
}
/// <summary>
/// 吃药的监听
/// </summary>
public static void Ation1() {
while (true)
{
string ctrl = Console.ReadLine();
if (ctrl.Equals("1")) {
Console.WriteLine("吃药,血量增加200点。");
}
}
}
/// <summary>
/// 攻击的监听
/// </summary>
public static void Ation2()
{
while (true)
{
string ctrl = Console.ReadLine();
if (ctrl.Equals("2"))
{
Console.WriteLine("攻击,对敌方造成200点伤害。");
}
}
}
}
}
当输入过快的时候无法被循环监听到:
一同运行,相当于两个线程会交替的进行执行。故而会出现如上现象。所以咱们开启线程的时候尽量进行同样的功能处理同样的事情。
演示代码:
代码语言:javascript复制using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace duo_0926
{
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(() => {
Ation1();
});
t1.Start();
Thread t2 = new Thread(() => {
Ation2();
});
t2.Start();
}
/// <summary>
/// 线程1
/// </summary>
public static void Ation1() {
for (int i = 0; i < 100; i )
{
Console.WriteLine("线程1:" i);
}
}
/// <summary>
/// 线程2
/// </summary>
public static void Ation2()
{
for (int i = 0; i < 100; i )
{
Console.WriteLine("线程2:" i);
}
}
}
}
执行效果:
以上效果可以说明,两个线程是相互交替执行,并非共同执行。
所以说,如果进行同时监听,无法正确进行线程操作。
通过如下方法即可进行线程交互:
代码语言:javascript复制Thread.Sleep(1);//休息1毫秒,不争抢线程
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace duo_0926
{
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(() => {
Ation1();
});
t1.Start();
Thread t2 = new Thread(() => {
Ation2();
});
t2.Start();
}
/// <summary>
/// 线程1
/// </summary>
public static void Ation1() {
for (int i = 0; i < 100; i )
{
Thread.Sleep(1);
Console.WriteLine("线程1:" i);
}
}
/// <summary>
/// 线程2
/// </summary>
public static void Ation2()
{
for (int i = 0; i < 100; i )
{
Thread.Sleep(1);
Console.WriteLine("线程2:" i);
}
}
}
}
可以看到,两个线程基本上都是在交互执行:
多次执行总会找到无法交互的地方。
说明线程休息的时间简短,线程1并没有争抢线程成功。可以加大线程休息时间。
Thread.Sleep(5);//每次线程休息5毫秒
重新测试,多次执行,出现争抢失败的情况就很少了:
重新测试监听:
代码语言:javascript复制using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace duo_0926
{
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(() => {
Console.WriteLine("启动吃药的监听:输入1代表吃药");
Ation1();
});
t1.Start();
Thread t2 = new Thread(() => {
Console.WriteLine("启动攻击的监听:输入2代表攻击");
Ation2();
});
t2.Start();
}
/// <summary>
/// 吃药的监听
/// </summary>
public static void Ation1()
{
while (true)
{
Thread.Sleep(1);
string ctrl = Console.ReadLine();
if (ctrl.Equals("1"))
{
Console.WriteLine("吃药,血量增加200点。");
}
}
}
/// <summary>
/// 攻击的监听
/// </summary>
public static void Ation2()
{
while (true)
{
Thread.Sleep(1);
string ctrl = Console.ReadLine();
if (ctrl.Equals("2"))
{
Console.WriteLine("攻击,对敌方造成200点伤害。");
}
}
}
}
}
创建web api项目进行接口测试
创建WebAPI项目,方便测试
修改【App_Start/WebApiConfig.cs】配置文件
添加取消XML返回配置
代码语言:javascript复制//取消XML返回
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
添加{action}访问路径设置
代码语言:javascript复制routeTemplate: "api/{controller}/{action}/{id}",
添加【API】接口:
依次选择【Web API】->【Web API 2 控制器 - 空】->【添加】:
输入文件名【Test】,点击【添加】
【TestController.cs】编码:
代码语言:javascript复制using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Web.Http;
namespace webApi.Controllers
{
public class TestController : ApiController
{
/// <summary>
/// 计时器
/// </summary>
public static int times = 0;
/// <summary>
/// 计时器开关
/// </summary>
public static bool ctrl = false;
/// <summary>
/// 开启计时器线程
/// </summary>
/// <param name="type">0、关闭<br/>1、开启</param>
/// <returns></returns>
[HttpGet]
public object StartTime(string type)
{
ctrl = type.Equals("0") ? false : Times();
return ctrl ? "启动成功" : "关闭成功";
}
/// <summary>
/// 获取计时的时间
/// </summary>
/// <returns></returns>
[HttpGet]
public object GetTime() {
return times;
}
/// <summary>
/// 开启计时
/// </summary>
/// <returns></returns>
private bool Times() {
Thread t = new Thread(()=>{
while (ctrl) {
Thread.Sleep(1000);//休息1秒
times = 1;
}
});
//启动
t.Start();
return true;
}
}
}
代码功能测试:
设为启动项:
启动项目:
选择【API】
两个接口都可以看到:
访问方式:
【http://localhost:2298/api/Test/StartTime?type=0】开启或关闭
【http://localhost:2298/api/Test/GetTime】获取时间
开启测试:
【http://localhost:2298/api/Test/StartTime?type=1】
稍等一会测试时间:【http://localhost:2298/api/Test/GetTime】
关闭测试:
可以看到试用多线程可以当然控制时间,控制开关操作,那么这类操作基本上都会在各种活动中用到,我之前的老总就喜欢各种各样的活动,每次我都会做类似的开关。当然,这个东西不仅仅用在这个地方,还有很多的环境都会用到,咱们要做到具体问题具体分析,可以在编码过程中为大家解决很多问题