进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,简单理解就是我们每打开一个应用程序就是在操作系统上开启了一个进程。
线程
操作系统能够进行运算调度的最小单位,包含在进程中,是进程中的实际运作单位,一个线程指的是进程中的单一顺序的控制流,一个进程可以并发多个线程,我们目前写的程序都在主线程中,简单理解就是代码从到下运行的一条“管道”
多线程
我们可以通过代码开启新的线程,可以同时运行多条“管道”,运行多个线程
申明一个新的线程
5.启动线程
默认启动的线程是前台线程,也就是会影响主线程结束的时机,默认是要等前台线程结束后,主线程才会结束,比如我们在新开线程中写一个死循环,这时候主线程中的代码运行结束了,本来应该要结束的,但是由于我们默认申明的是前台线程,所以必须等新开线程结束后主线程才会结束。
6.将线程设置为后台线程
设置为后台线程后,主线程代码如果运行结束会强制终止还在运行的后台线程
7.终止线程
我们可以在线程的代码中加一个循环,循环的判断条件是是我主线程中提供的一个布尔变量,当我们想要关闭线程的时候直接将该变量设置为false就可以了,这时候新线程中的代码就会跳出循环停止运行
8.线程休眠
让线程休眠多少毫秒,1s=1000毫秒,Thread.Sleep(填毫秒值),在哪个线程中执行就会休眠对应的线程,不会干扰到其他线程
9.线程之间共享数据
多个线程使用的内存是共享的,都属于该进程,所以当多个线程同时操作一片内存区域时可能会出现问题,
使用加锁lock的形式,将线程中的代码锁住,比如程序如果是在运行主线程中的代码,这时候会将lock中的代码进行加锁,也就是在其他加锁的地方必须等这边的代码执行完才会去执行另外一个线程中加锁的代码
线程练习题
控制台有一个■通过键盘上的↑,↓,←,→键控制方块的移动,通过开启一个多线程来检测输入控制它的转向,而且要判断边界,方块不能超出边界。
- 提示:检测键盘上的按键我们可以通过Console.ReadKey().key和ConsoleKey.按键名来判断输入的是哪个按键来改变对应方块移动的方向
using System.Collections;
namespace vrDemo0208am3
{
//创建一个方块类
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, 10) * 2;
int y = random.Next(0, 10);
block.x = x;
block.y = y;
block.draw();
}
static void Main(string[] args)
{
Console.CursorVisible = false;
block.draw();
list.Add(head);
list[0].draw();
//创建一个线程
Thread T = new Thread(ThreadLogic);
T.Start();
T.IsBackground = true;
while (true)
{
Thread.Sleep(200);
//在方块的坐标还没改变之前先获取当前列表最后一个元素的坐标
int x = list[list.Count - 1].x;
int y = list[list.Count - 1].y;
list[0].clear();
//清除所有尾巴的x,y坐标
//变化的坐标
//所有尾巴要改变坐标为前面的方块的x,y坐标
for (int i = list.Count - 1; i >= 1; i--) //3个
{
list[i].clear();//清除原来的位置
//更新坐标为前一个方块没更新之前的坐标位置
list[i].x = list[i - 1].x;
list[i].y = list[i - 1].y;
}
list[0].move();
//在什么时机去判断碰撞是不是我现在头移动到下个位置刚好是不动方块的坐标
if (block.x == head.x && block.y == head.y)
{
//添加新的方块到列表中 这个时候的坐标应该最后的方块上一次的位置
list.Add(new Head(x / 2, y));//因为我们初始化的时候给x乘于二了,所以传进来的时候要除以二
//更新一下新的方块的位置
update();
}
for (int i = 0; i < list.Count; i++)
{
list[i].draw();
}
}
}
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;
}
}
}
}
}