一、进程和线程
1.什么是进程?
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,简单理解就是我们每打开一个应用程序就是在操作系统上开启了一个进程。
2.什么是线程
操作系统能够进行运算调度的最小单位,包含在进程中,是进程中的实际运作单位,一个线程指的是进程中的单一顺序的控制流,一个进程可以并发多个线程,我们目前写的程序都在主线程中,简单理解就是代码从到下运行的一条“管道”
3.什么是多线程
我们可以通过代码开启新的线程,可以同时运行多条“管道”,运行多个线程
4.申明一个新的线程
开辟一个新的线程用的是线程了Thread,需要引用using System.Threading命名空间,开辟的线程的代码逻辑是放到传进来的函数中的,所以申明一个线程需要传入一个函数。
5.启动线程
默认启动的线程是前台线程,也就是会影响主线程结束的时机,默认是要等前台线程结束后,主线程才会结束,比如我们在新开线程中写一个死循环,这时候主线程中的代码运行结束了,本来应该要结束的,但是由于我们默认申明的是前台线程,所以必须等新开线程结束后主线程才会结束。
6.将线程设置为后台线程
设置为后台线程后,主线程代码如果运行结束会强制终止还在运行的后台线程
7.终止线程
我们可以在线程的代码中加一个循环,循环的判断条件是是我主线程中提供的一个布尔变量,当我们想要关闭线程的时候直接将该变量设置为false就可以了,这时候新线程中的代码就会跳出循环停止运行
8.线程休眠
让线程休眠多少毫秒,1s=1000毫秒,Thread.Sleep(填毫秒值),在哪个线程中执行就会休眠对应的线程,不会干扰到其他线程
9.线程之间共享数据
多个线程使用的内存是共享的,都属于该进程,所以当多个线程同时操作一片内存区域时可能会出现问题,
使用加锁lock的形式,将线程中的代码锁住,比如程序如果是在运行主线程中的代码,这时候会将lock中的代码进行加锁,也就是在其他加锁的地方必须等这边的代码执行完才会去执行另外一个线程中加锁的代码
所有代码汇总:
namespace 进程1028
{
internal class Program
{
public static int exp = 0; //经验值
public static object obj = new object();
static void Main(string[] args)
{
//进程,线程
//新建一个线程 另外一个运行代码的管道,需要给入口
//主线程的入口是main函数
Thread t = new Thread(ThreadLogic);
//开启线程
t.Start();
//线程作用:阻碍运行的代码可以放入线程中去执行。
//每个线程是不是公用一个内存中的数据,会不会出现混乱
//解决方法:给公用数据的地方加个锁 lock,如果不用同一个数据就不用加lock
while (true)
{
/*Thread.Sleep(1000);
Console.WriteLine("敲打");*/
lock (obj)
//保证锁里面的代码运行完之后才会执行另一个锁里面的代码
{
Thread.Sleep(1000);
Console.SetCursorPosition(0, 0);
Console.WriteLine(1);
}
}
//当主线程中的所有代码运行结束我们要强制关闭这个线程怎么办,
//我们可以给新开的线程设置为后台线程,强制关闭后台,所有的线程都会关闭
t.IsBackground = true;
Console.WriteLine("停止");
//Thread.Sleep(1000);
//表示当我们按下按键的时候才会执行下面的程序,用来捕获按键
//没按的时候主线程还没有结束
Console.ReadKey();
//监测用户按下的键:Console.ReadKey().Key
Console.WriteLine(Console.ReadKey().Key == ConsoleKey.Enter);
Thread.Sleep(1000);
}
static void ThreadLogic()
{
//开辟另一个管道,在里面执行
/* while (true)
{
*//*Thread.Sleep(1000);
exp++;
Console.WriteLine($"经验值+1:{exp}");*//*
lock (obj)
{
Thread.Sleep(1000);
Console.SetCursorPosition(10, 5);
Console.WriteLine(2);
}
}*/
}
}
}
10.线程练习题
控制台有一个■通过键盘上的↑,↓,←,→键控制方块的移动,通过开启一个多线程来检测输入控制它的转向,而且要判断边界,方块不能超出边界。
- 提示:检测键盘上的按键我们可以通过Console.ReadKey().key和ConsoleKey.按键名来判断输入的是哪个按键来改变对应方块移动的方向
using System.Collections;
namespace demo1028am
{
//创建一个方块类
public class Block
{
public Block(int x, int y)
{
this.x = x * 2;
this.y = y;
}
public int x;
public int y;
public ConsoleColor color = ConsoleColor.White;
//方块都有一个画的方法
public void draw()
{
Console.SetCursorPosition(this.x, this.y);
Console.ForegroundColor = color;
Console.Write("■");
}
//清除的方法
public void clear()
{
Console.SetCursorPosition(this.x, this.y);
Console.Write(" ");
}
}
//创建一个子类 头
public class Head : Block
{
public Head(int x, int y, Dir dir = Dir.right) : base(x, y)
{
this.dir = dir;
base.color = ConsoleColor.Red;
}
public Dir dir;
//移动的方法
public void move()
{
switch (dir)
{
case Dir.up:
y--;
break;
case Dir.down:
y++;
break;
case Dir.left:
x -= 2;
break;
case Dir.right:
x += 2;
break;
}
}
//碰撞检测的方法
public bool crash(Block block)
{
if (block.x == this.x && block.y == this.y)
{
return true;
}
return false;
}
}
//创建一个枚举来存方向
public enum Dir
{
up,down,left,right
}
internal class Program
{
public static object obj = new object();
public static int width = Console.BufferWidth;//屏幕宽度
public static int height = Console.BufferHeight;//屏幕高度
//创建一个不动方块对象
public static Block block = new Block(5, 5);
//创建一个头
public static Head head = new Head(0, 0);
//创建一个列表存每一个方块
public static List<Head> list = new List<Head>();
//更新不动的方块
public static void update()
{
Random random = new Random();
int x = random.Next(0,30)*2;
int y = random.Next(0,30);
block.x = x;
block.y = y;
block.draw();
}
static void Main(string[] args)
{
Console.CursorVisible = false;
list.Add(head);
list[0].draw();
//创建一个线程
Thread T = new Thread(ThreadLogic);
Thread T2 = new Thread(ThreadLogic2);
T2.Start();
T.Start();
T.IsBackground = true;
T2.IsBackground = true;
while (true)
{
lock (obj)
{
Thread.Sleep(500);
list[0].clear();
list[0].move();
list[0].draw();
}
}
}
static void ThreadLogic2()
{
block.draw();
while (true)
{
lock (obj)
{
if (list[0].crash(block))
{
update();
};//碰到完要干嘛 更新block的位置
}
}
}
static void ThreadLogic()
{
while (true)
{
switch (Console.ReadKey(true).Key)//检测用户输入的按键
{
case ConsoleKey.UpArrow:
//只有一个头随便跑 如果不是一个方块那么不能往反方向走
if (head.dir != Dir.down || list.Count == 1) head.dir = Dir.up;
break;
case ConsoleKey.DownArrow:
//只有一个头随便跑 如果不是一个方块那么不能往反方向走
if (head.dir != Dir.up || list.Count == 1) head.dir = Dir.down;
break;
case ConsoleKey.LeftArrow:
//只有一个头随便跑 如果不是一个方块那么不能往反方向走
if (head.dir != Dir.right || list.Count == 1) head.dir = Dir.left;
break;
case ConsoleKey.RightArrow:
//只有一个头随便跑 如果不是一个方块那么不能往反方向走
if (head.dir != Dir.left || list.Count == 1) head.dir = Dir.right;
break;
}
}
}
}
}