首先本人是个编程菜鸟
我想实现效果:一排图片从左上角往右下角移动,周而复始。
试了一下,在FORM中创建16个picturebox然后循环移动所有的picturebox很卡。所以想用多线程实现,每个线程创建一个picturebox,然后控制它往右下角走,每50ms走一步,但是我写的程序有问题,窗口中显示不出创建的pictruebox
请大家指点一下,拜谢! public partial class Form1 : Form
{
Point fa = new Point(-120, -80);
public Form1()
{
InitializeComponent();
} private void Form1_Load(object sender, EventArgs e)
{
Thread thr = new Thread(showpic);
thr.Start();
} void showpic()
{
PictureBox picb = new PictureBox();
picb.ImageLocation = "picture\\IMG_20181014_100220.jpg";
picb.Location = fa;
picb.Size = new Size(120, 80);
picb.SizeMode = PictureBoxSizeMode.Zoom;
while(true)
{
picb.Location = new Point(picb.Location.X + 16, picb.Location.Y - 9);
if (picb.Location.X > 1920)
picb.Location = fa;
Thread.Sleep(50);
}
}
}
我想实现效果:一排图片从左上角往右下角移动,周而复始。
试了一下,在FORM中创建16个picturebox然后循环移动所有的picturebox很卡。所以想用多线程实现,每个线程创建一个picturebox,然后控制它往右下角走,每50ms走一步,但是我写的程序有问题,窗口中显示不出创建的pictruebox
请大家指点一下,拜谢! public partial class Form1 : Form
{
Point fa = new Point(-120, -80);
public Form1()
{
InitializeComponent();
} private void Form1_Load(object sender, EventArgs e)
{
Thread thr = new Thread(showpic);
thr.Start();
} void showpic()
{
PictureBox picb = new PictureBox();
picb.ImageLocation = "picture\\IMG_20181014_100220.jpg";
picb.Location = fa;
picb.Size = new Size(120, 80);
picb.SizeMode = PictureBoxSizeMode.Zoom;
while(true)
{
picb.Location = new Point(picb.Location.X + 16, picb.Location.Y - 9);
if (picb.Location.X > 1920)
picb.Location = fa;
Thread.Sleep(50);
}
}
}
源码 动态创建的 picturebox 控件都没有装到 Form1 中, 显个毛 ,不过,按你的源码来看,即使显示,也是卡的,
不是这样的 显示分步显示图片的
Point fa = new Point(0, 0);
private void Form1_Load(object sender, EventArgs e)
{
Task.Run(() => { showpic(); }); }
void showpic()
{
PictureBox picb = new PictureBox();
picb.ImageLocation = "Image\\1.jpg";
picb.Location = fa;
picb.Size = new Size(120, 80);
picb.SizeMode = PictureBoxSizeMode.Zoom;
Invoke(new EventHandler(delegate { Controls.Add(picb); }));
while (true)
{
Invoke(new EventHandler(delegate
{
picb.Location = new Point(picb.Location.X + 16, picb.Location.Y + 9);
if (picb.Location.X > 1920)
picb.Location = fa;
}));
Thread.Sleep(50);
}
}
Controls.Add(picb);2.while循环内嵌Sleep是不对的,你可以使用timer:Timer timer = new Timer
{
Interval = 50,
Enabled = true
};
timer.Tick += timer1_Tick;
private void timer1_Tick(object sender, System.EventArgs e)
{
picb.Location = new Point(picb.Location.X + 16, picb.Location.Y - 9);
if (picb.Location.X > 1920)
picb.Location = fa;
}
3.Y坐标一直在减,一直在form外 怎么显示?
{
var label = new Label();
this.Controls.Add(label);
var rnd = new Random();
var x = (int)(rnd.NextDouble() * 50) - 25;
var y = (int)(rnd.NextDouble() * 50) - 25;
while (true)
{
label.Text = DateTime.Now.ToString("mm:ss");
label.Top += y;
label.Left += x;
if (label.Left >= 0 && label.Left <= this.ClientSize.Width && label.Top >= 0 && label.Top <= this.ClientSize.Height)
await Task.Delay(500);
else
{
label.Top -= y;
label.Left -= x;
x = (int)(rnd.NextDouble() * 50) - 25;
y = (int)(rnd.NextDouble() * 50) - 25;
}
}
}
这里跟什么线程没有直接关系。这里最主要地是,在 Task.Delay 语句执行时,方法 Form_Load 就已经结束了;然后500毫秒之后执行到 label.Text = DateTim.... 语句时,是异步调用的。这其实是基于“事件发生时才回调委托”的概念,要知道交互操作是异步编程的,而不是阻塞的概念。
{
var label = new Label();
this.Controls.Add(label);
void 设置方向(out int a, out int b)
{
var rnd = new Random();
a = (int)(rnd.NextDouble() * 50) - 25;
b = (int)(rnd.NextDouble() * 50) - 25;
}
设置方向(out int x, out int y);
while (true)
{
label.Text = DateTime.Now.ToString("mm:ss");
label.Top += y;
label.Left += x;
if (label.Left >= 0 && label.Left <= this.ClientSize.Width && label.Top >= 0 && label.Top <= this.ClientSize.Height)
await Task.Delay(500);
else
{
label.Top -= y;
label.Left -= x;
设置方向(out x, out y);
}
}
}
但是语法是小伎俩,关键是基本的理念。一个语法学起来可能需要1分钟,而一个理念需要你学习5年。
这样说说似乎就有点偏激了,很多程序本身就是一个死循环。
驱动一个事件要么是死循环,要么刷Timer。比如你让一个设备重复一个抓料放料动作,它本身就是一个死循环。
你判断一个动作是否到位,要么用死循环+Sleep(必须+Sleep),要么刷Timer.
1、Thread.Sleep 是同步延迟,Task.Delay异步延迟。2、Thread.Sleep 会阻塞线程,Task.Delay不会。3、Thread.Sleep不能取消,Task.Delay可以。4. Task.Delay() 比 Thread.Sleep() 消耗更多的资源,但是Task.Delay()可用于为方法返回Task类型;或者根据CancellationToken取消标记动态取消等待5. Task.Delay() 实质创建一个运行给定时间的任务, Thread.Sleep() 使当前线程休眠给定时间。
这说明你完全不理解事件。交互程序运行中自然是几十万、无数的事件触发,生生不息地不断触发的。但是僵化的说法是看不到运动前进,只能看到诡异地反复。例如上面的 async void Form_Load 过程例子,当执行到 Delay(500) 的时候,立刻,这个过程就结束了。事件只是注册一个回调而已。如果连注册回调跟阻塞调用都分不清楚,自然就会混淆编程概念。
和楼主的 while (true) + Sleep(500) 究竟有多大区别呢?
我想你是否应该科普一下呢?
这说明你完全不理解事件。交互程序运行中自然是几十万、无数的事件触发,生生不息地不断触发的。但是僵化的说法是看不到运动前进,只能看到诡异地反复。例如上面的 async void Form_Load 过程例子,当执行到 Delay(500) 的时候,立刻,这个过程就结束了。事件只是注册一个回调而已。如果连注册回调跟阻塞调用都分不清楚,自然就会混淆编程概念。
处理发货;这样的代码。包括我们简单地去调用一个多线程 I/O 方法去网上搜索点网页时,使用异步操作,也是如此。这类代码大量被使用、被设计出来。在过去,这样的回调委托代码,我们可能只能明确地用方法的回调机制、事件注册机制来实现。现在可以用 async/await 语法。但是首先需要理解其机制才能写出来明白的 async/await 代码。假设说让你画一个程序流程图,你会用流程图符号来画出同步顺序操作、跟异步回调操作(上面的 await 语法)的本质的区别吗?如果你不能在流程图上区分出来,那么肯定在语言和设计上无法区分出来同步阻塞跟异步回调的区别,那么你面对上面的那种操作设计需求,可能就无法异步,甚至可能连使用子线程去处理信用卡都不能理解。其实代码不是最重要的,如果先来画个流程图、用语言描述一下异步操作都弄不好,这才是问题。
处理发货;
这样的程序,当执行到 await 的时候,这个过程就结束了,当前线程已经离开这个过程而去执行过程之后的其它过程去了!然后也许200毫秒之后,也许8秒钟之后......总之是不确定的时间之后,系统才会再来回调执行等号赋值以及之后的代码,而那个时候也许是使用相同线程、也许是其它线程来回调。异步操作不一定就是使用子线程来执行的。但是更重要地是,这里没有什么“阻塞”,这个 await 动做根本不占用任何线程,所以 await 是大量协程管理的基础技术,async/await 它可以避免滥用线程。
{
abc.Localtion = 新的坐标位置;
Sleep(100);
}这类死循环代码。接下来,知道这是个坑,就开始觉得线程是个好东西了,满脑子只有线程概念了。其实早期的 VB 等等工具,以及大量的游戏软件,都是单线程的,都在 UI 主线程就能编写复杂的、世界流行的各种游戏。学过 UI 设计知识的编程者知道用 Timer 来控制游戏流程的每一帧的动做,这就是交互式程序基本概念,不需要纠结子线程。有了这类设计概念,懂得了交互世界的基本道理。而什么“多线程”此时并不重要。
这说明你完全不理解事件。交互程序运行中自然是几十万、无数的事件触发,生生不息地不断触发的。但是僵化的说法是看不到运动前进,只能看到诡异地反复。例如上面的 async void Form_Load 过程例子,当执行到 Delay(500) 的时候,立刻,这个过程就结束了。事件只是注册一个回调而已。如果连注册回调跟阻塞调用都分不清楚,自然就会混淆编程概念。说那些无用的理论干什么? 就来实现一件事,简单的小事:一台设备,有个“启动”按钮(硬件),要求按下“启动”按钮,设备运行,该怎么做?你别谈理论,就实际解决问题,请问有什么高明的办法? (我的方法要么循环+sleep,要么刷Timer)
这说明你完全不理解事件。交互程序运行中自然是几十万、无数的事件触发,生生不息地不断触发的。但是僵化的说法是看不到运动前进,只能看到诡异地反复。例如上面的 async void Form_Load 过程例子,当执行到 Delay(500) 的时候,立刻,这个过程就结束了。事件只是注册一个回调而已。如果连注册回调跟阻塞调用都分不清楚,自然就会混淆编程概念。说那些无用的理论干什么? 就来实现一件事,简单的小事:一台设备,有个“启动”按钮(硬件),要求按下“启动”按钮,设备运行,该怎么做?你别谈理论,就实际解决问题,请问有什么高明的办法? (我的方法要么循环+sleep,要么刷Timer)
sp已经跟你说了很多次他的解决方案了:
你按照你自己需求设计就会发现你的需求是这样的:
a 启动按钮点击触发一个 await的硬件访问事件,这个事件结束后触发结束后的事件(正常如何,异常如何)。
b 因为你使用了await,硬件事件启动后界面不会锁死,当硬件事件结束后根据运行结果自动触发对应的绑定事件继续处理你的业务。
c 你只需要在硬件事件结束后成功再次执行自己的扫描任务就可以实现循环访问硬件的功能。
d 做这些的时候注意做一个强制停止和退出的逻辑在里面这样就可以在关闭的时候关闭硬件访问等防止硬件长时间占用了。
你有认真读他反复给你说的东西吗?没有,你只是想看到实际解决方案,但是有时候有些东西的解决方案就是要冗长的语言描述的,并不是几行代码。
简单组织一下:
假设你的程序只有10个线程,
如果代码是用下面这种写法,那么你的程序同一时间,只能对最多10个用户提供服务,因为线程是同步的,进来10个用户后,10个线程被被他们长期持有了,且无法退出:while(true){
Thread.Sleep(1000);
xxx业务代码;
}但是如果你的程序是下面这种写法,你的程序就可以对超过10个用户提供服务,因为在await的时候,你的线程已经被暂时释放了,可以给其它用户提供服务,等时间到了,再重新分配一个线程,回来处理你的后续业务代码:while(true){
await Task.Delay(1000);
xxx业务代码;
}