1、进程
首先打开任务管理器,查看当前运行的进程:
从任务管理器里面可以看到当前所有正在运行的进程。那么究竟什么是进程呢?
进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程。线程是操作系统分配处理器时间的基本单元,在进程中可以有多个线程同时执行代码。进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进程可以理解为一个程序的基本边界。是应用程序的一个运行例程,是应用程序的一次动态执行过程。
2、线程
在任务管理器里面查询当前总共运行的线程数:
线程(Thread)是进程中的基本执行单元,是操作系统分配CPU时间的基本单位,一个进程可以包含若干个线程,在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法作为入口的,当调用此方法时系统就会自动创建一个主线程。线程主要是由CPU寄存器、调用栈和线程本地存储器(Thread Local Storage,TLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状态信息。
3、多线程
多线程的优点:可以同时完成多个任务;可以使程序的响应速度更快;可以让占用大量处理时间的任务或当前没有进行处理的任务定期将处理时间让给别的任务;可以随时停止任务;可以设置每个任务的优先级以优化程序性能。
那么可能有人会问:为什么可以多线程执行呢?总结起来有下面两方面的原因:
- 1、CPU运行速度太快,硬件处理速度跟不上,所以操作系统进行分时间片管理。这样,从宏观角度来说是多线程并发的,因为CPU速度太快,察觉不到,看起来是同一时刻执行了不同的操作。但是从微观角度来讲,同一时刻只能有一个线程在处理。
- 2、目前电脑都是多核多CPU的,一个CPU在同一时刻只能运行一个线程,但是多个CPU在同一时刻就可以运行多个线程。
- 然而,多线程虽然有很多优点,但是也必须认识到多线程可能存在影响系统性能的不利方面,才能正确使用线程。不利方面主要有如下几点:
- (1)线程也是程序,所以线程需要占用内存,线程越多,占用内存也越多。
- (2)多线程需要协调和管理,所以需要占用CPU时间以便跟踪线程。
- (3)线程之间对共享资源的访问会相互影响,必须解决争用共享资源的问题。
- (4)线程太多会导致控制太复杂,最终可能造成很多程序缺陷。
当启动一个可执行程序时,将创建一个主线程。在默认的情况下,C#程序具有一个线程,此线程执行程序中以Main方法开始和结束的代码,Main()方法直接或间接执行的每一个命令都有默认线程(主线程)执行,当Main()方法返回时此线程也将终止。
一个进程可以创建一个或多个线程以执行与该进程关联的部分程序代码。在C#中,线程是使用Thread类处理的,该类在System.Threading命名空间中。使用Thread类创建线程时,只需要提供线程入口,线程入口告诉程序让这个线程做什么。通过实例化一个Thread类的对象就可以创建一个线程。创建新的Thread对象时,将创建新的托管线程。Thread类接收一个ThreadStart委托或ParameterizedThreadStart委托的构造函数,该委托包装了调用Start方法时由新线程调用的方法,示例代码如下:
Thread thread=new Thread(new ThreadStart(method));//创建线程
thread.Start(); //启动线程
上面代码实例化了一个Thread对象,并指明将要调用的方法method(),然后启动线程。ThreadStart委托中作为参数的方法不需要参数,并且没有返回值。ParameterizedThreadStart委托一个对象作为参数,利用这个参数可以很方便地向线程传递参数,示例代码如下:
Thread thread=new Thread(new ParameterizedThreadStart(method));//创建线程 这里传参的是 方法函数
thread.Start(3); //启动线程 这里传参的是 函数所需要的参数
创建多线程的步骤:
1、编写线程所要执行的方法
2、实例化Thread类,并传入一个指向线程所要执行方法的委托。(这时线程已经产生,但还没有运行)
3、调用Thread实例的Start方法,标记该线程可以被CPU执行了,但具体执行时间由CPU决定
5、实操
class Program
{
static void Main(string[] args)
{
//创建无参的线程
Thread thread1 = new Thread(new ThreadStart(Thread1));
//调用Start方法执行线程
thread1.Start();
Console.ReadKey();
}
/// <summary>
/// 创建无参的方法
/// </summary>
static void Thread1()
{
Console.WriteLine("这是无参的方法");
}
}
运行结果
除了可以运行静态的方法,还可以运行类方法
class Program
{
static void Main(string[] args)
{
//创建ThreadTest类的一个实例
ThreadTest test=new ThreadTest();
//调用test实例的MyThread方法
Thread thread = new Thread(new ThreadStart(test.MyThread));
//启动线程
thread.Start();
Console.ReadKey();
}
}
class ThreadTest
{
public void MyThread()
{
Console.WriteLine("这是一个实例方法");
}
}
运行结果:
如果为了简单,也可以通过匿名委托或Lambda表达式来为Thread的构造方法赋值
static void Main(string[] args)
{
//通过匿名委托创建
Thread thread1 = new Thread(delegate() { Console.WriteLine("我是通过匿名委托创建的线程"); });
thread1.Start();
//通过Lambda表达式创建
Thread thread2 = new Thread(() => Console.WriteLine("我是通过Lambda表达式创建的委托"));
thread2.Start();
Console.ReadKey();
}
运行结果:
ParameterizedThreadStart是一个有参的、返回值为void的委托,定义如下:
public delegate void ParameterizedThreadStart(Object obj)
class Program
{
static void Main(string[] args)
{
//通过ParameterizedThreadStart创建线程
Thread thread = new Thread(new ParameterizedThreadStart(Thread1));
//给方法传值
thread.Start("这是一个有参数的委托");
Console.ReadKey();
}
/// <summary>
/// 创建有参的方法
/// 注意:方法里面的参数类型必须是Object类型
/// </summary>
/// <param name="obj"></param>
static void Thread1(object obj)
{
Console.WriteLine(obj);
}
}
4. 线程的状态
通过ThreadState可以检测线程是处于Unstarted、Sleeping、Running 等等状态,它比 IsAlive 属性能提供更多的特定信息。
前面说过,一个应用程序域中可能包括多个上下文,而通过CurrentContext可以获取线程当前的上下文。
CurrentThread是最常用的一个属性,它是用于获取当前运行的线程。
5. System.Threading.Thread的方法
Thread 中包括了多个方法来控制线程的创建、挂起、停止、销毁,以后来的例子中会经常使用。
方法名称 | 说明 |
---|---|
Abort() | 终止本线程。 |
GetDomain() | 返回当前线程正在其中运行的当前域。 |
GetDomainId() | 返回当前线程正在其中运行的当前域Id。 |
Interrupt() | 中断处于 WaitSleepJoin 线程状态的线程。 |
Join() | 已重载。 阻塞调用线程,直到某个线程终止时为止。 |
Resume() | 继续运行已挂起的线程。 |
Start() | 执行本线程。 |
Suspend() | 挂起当前线程,如果当前线程已属于挂起状态则此不起作用 |
Sleep() | 把正在运行的线程挂起一段时间。 |
6. 前台线程和后台线程
前台线程:只有所有的前台线程都结束,应用程序才能结束。默认情况下创建的线程
都是前台线程
后台线程:只要所有的前台线程结束,后台线程自动结束。通过Thread.IsBackground设置后台线程。必须在调用Start方法之前设置线程的类型,否则一旦线程运行,将无法改变其类型。
通过BeginXXX方法运行的线程都是后台线程。
class Program
{
static void Main(string[] args)
{
//演示前台、后台线程
BackGroundTest background = new BackGroundTest(10);
//创建前台线程
Thread fThread = new Thread(new ThreadStart(background.RunLoop));
//给线程命名
fThread.Name = "前台线程";
BackGroundTest background1 = new BackGroundTest(20);
//创建后台线程
Thread bThread = new Thread(new ThreadStart(background1.RunLoop));
bThread.Name = "后台线程";
//设置为后台线程
bThread.IsBackground = true;
//启动线程
fThread.Start();
bThread.Start();
}
}
class BackGroundTest
{
private int Count;
public BackGroundTest(int count)
{
this.Count = count;
}
public void RunLoop()
{
//获取当前线程的名称
string threadName = Thread.CurrentThread.Name;
for (int i = 0; i < Count; i++)
{
Console.WriteLine("{0}计数:{1}",threadName,i.ToString());
//线程休眠500毫秒
Thread.Sleep(1000);
}
Console.WriteLine("{0}完成计数",threadName);
}
}
运行结果:前台线程执行完,后台线程未执行完,程序自动结束。
把bThread.IsBackground = true注释掉,运行结果:主线程执行完毕后(Main函数),程序并未结束,而是要等所有的前台线程结束以后才会结束。
后台线程一般用于处理不重要的事情,应用程序结束时,后台线程是否执行完成对整个应用程序没有影响。如果要执行的事情很重要,需要将线程设置为前台线程。
7. 线程同步
所谓同步:是指在某一时刻只有一个线程可以访问变量。
如果不能确保对变量的访问是同步的,就会产生错误。
c#为同步访问变量提供了一个非常简单的方式,即使用c#语言的关键字Lock,它可以把一段代码定义为互斥段,互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。在c#中,关键字Lock定义如下:
Lock(expression)
{
statement_block
}
expression代表你希望跟踪的对象:
如果你想保护一个类的实例,一般地,你可以使用this;
如果你想保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以了
而statement_block就算互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。
以书店卖书为例
class Program
{
static void Main(string[] args)
{
BookShop book = new BookShop();
//创建两个线程同时访问Sale方法
Thread t1 = new Thread(new ThreadStart(book.Sale));
Thread t2 = new Thread(new ThreadStart(book.Sale));
//启动线程
t1.Start();
t2.Start();
Console.ReadKey();
}
}
class BookShop
{
//剩余图书数量
public int num = 1;
public void Sale()
{
int tmp = num;
if (tmp > 0)//判断是否有书,如果有就可以卖
{
Thread.Sleep(1000);
num -= 1;
Console.WriteLine("售出一本图书,还剩余{0}本", num);
}
else
{
Console.WriteLine("没有了");
}
}
}
运行结果:
从运行结果可以看出,两个线程同步访问共享资源,没有考虑同步的问题,结果不正确。
考虑线程同步,改进后的代码:
class Program
{
static void Main(string[] args)
{
BookShop book = new BookShop();
//创建两个线程同时访问Sale方法
Thread t1 = new Thread(new ThreadStart(book.Sale));
Thread t2 = new Thread(new ThreadStart(book.Sale));
//启动线程
t1.Start();
t2.Start();
Console.ReadKey();
}
}
class BookShop
{
//剩余图书数量
public int num = 1;
public void Sale()
{
//使用lock关键字解决线程同步问题
lock (this)
{
int tmp = num;
if (tmp > 0)//判断是否有书,如果有就可以卖
{
Thread.Sleep(1000);
num -= 1;
Console.WriteLine("售出一本图书,还剩余{0}本", num);
}
else
{
Console.WriteLine("没有了");
}
}
}
}
运行结果:
8. 跨线程访问
点击“测试”,创建一个线程,从0循环到10000给文本框赋值,代码如下:
private void btn_Test_Click(object sender, EventArgs e)
{
//创建一个线程去执行这个方法:创建的线程默认是前台线程
Thread thread = new Thread(new ThreadStart(Test));
//Start方法标记这个线程就绪了,可以随时被执行,具体什么时候执行这个线程,由CPU决定
//将线程设置为后台线程
thread.IsBackground = true;
thread.Start();
}
private void Test()
{
for (int i = 0; i < 10000; i++)
{
this.textBox1.Text = i.ToString();
}
}
运行结果:
产生错误的原因:textBox1是由主线程创建的,thread线程是另外创建的一个线程,在.NET上执行的是托管代码,C#强制要求这些代码必须是线程安全的,即不允许跨线程访问Windows窗体的控件。
解决方案:
1、在窗体的加载事件中,将C#内置控件(Control)类的CheckForIllegalCrossThreadCalls属性设置为false,屏蔽掉C#编译器对跨线程调用的检查。
private void Form1_Load(object sender, EventArgs e)
{
//取消跨线程的访问
Control.CheckForIllegalCrossThreadCalls = false;
}
使用上述的方法虽然可以保证程序正常运行并实现应用的功能,但是在实际的软件开发中,做如此设置是不安全的(不符合.NET的安全规范),在产品软件的开发中,此类情况是不允许的。如果要在遵守.NET安全标准的前提下,实现从一个线程成功地访问另一个线程创建的空间,要使用C#的方法回调机制。
namespace MultiThreadDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//定义回调
private delegate void setTextValueCallBack(int value);
//声明回调
private setTextValueCallBack setCallBack;
private void btn_Test_Click(object sender, EventArgs e)
{
//实例化回调
setCallBack = new setTextValueCallBack(SetValue);
//创建一个线程去执行这个方法:创建的线程默认是前台线程
Thread thread = new Thread(new ThreadStart(Test));
//Start方法标记这个线程就绪了,可以随时被执行,具体什么时候执行这个线程,由CPU决定
//将线程设置为后台线程
thread.IsBackground = true;
thread.Start();
}
private void Test()
{
for (int i = 0; i < 10000; i++)
{
//使用回调
textBox1.Invoke(setCallBack, i);
}
}
/// <summary>
/// 定义回调使用的方法
/// </summary>
/// <param name="value"></param>
private void SetValue(int value)
{
this.textBox1.Text = value.ToString();
}
}
}
回调是一种委托,委托是一种线程
再来看一个单独的回调例子
public delegate int MyDelegate(int x, int y);
class Delegate_Demo {
static void Main(string[] args) {
int sum = MyAdd(1, 2, new Helper().Add);
Console.WriteLine(sum);
}
private static int MyAdd(int a, int b, MyDelegate myDele) {
return myDele(a, b);
}
}
class Helper {
public Helper() {}
public int Add(int num1, int num2) {
return num1 + num2;
}
}
故,采用回调的时候,相当于new了一个线程,但是又因为回调是delegate,所以可以跨线程修改其他线程对象,比如修改主线程UI
----最后我们用大恒的双相机采集来结束这一课
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
using GxIAPINET;
using GxIAPINET.Sample.Common;
namespace GxMultiCam
{
public partial class ImageShowFrom : Form
{
CFps m_objCFps = new CFps(); ///<统计帧率的对象
IGXDevice m_objIGXDevice = null; ///<设备对像
IGXStream m_objIGXStream = null; ///<流对像
IGXFeatureControl m_objIGXFeatureControl = null; ///<远端设备属性控制器对像 ///<图像显示类对象
string m_strSN = ""; ///<序列号
bool m_bIsColor = false; ///<是否支持彩色相机
byte[] m_byMonoBuffer = null; ///<黑白相机buffer
byte[] m_byColorBuffer = null; ///<彩色相机buffer
int m_nWidth = 0; ///<图像宽度
int m_nHeigh = 0; ///<图像高度
Bitmap m_bitmap = null; ///<bitmap对象
const uint PIXEL_FORMATE_BIT = 0x00FF0000; ///<用于与当前的数据格式进行与运算得到当前的数据位数
const uint GX_PIXEL_8BIT = 0x00080000; ///<8位数据图像格式
const int CP_NOCLOSE_BUTTON = 0x200; ///<用于禁用窗体的关闭按钮
int m_nOperateID = 0; ///<选择的相机号
GxBitmap m_objGxBitmap = null; ///<图像显示类对象 ///
public ImageShowFrom(IGXDevice objIGXDevice, int nOperateID)
{
m_objIGXDevice = objIGXDevice;
m_nOperateID = nOperateID + 1;
Init();
//使用双缓冲,让图像显示不闪烁
SetStyle(
ControlStyles.OptimizedDoubleBuffer
| ControlStyles.ResizeRedraw
| ControlStyles.Selectable
| ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint
| ControlStyles.SupportsTransparentBackColor,
true);
InitializeComponent();
}
/// <summary>
/// 显示图像等过程的初始化操作
/// </summary>
void Init()
{
m_strSN = m_objIGXDevice.GetDeviceInfo().GetSN();
string strValue = null;
if (null != m_objIGXDevice)
{
m_nWidth = (int)m_objIGXDevice.GetRemoteFeatureControl().GetIntFeature("Width").GetValue();
m_nHeigh = (int)m_objIGXDevice.GetRemoteFeatureControl().GetIntFeature("Height").GetValue();
m_byMonoBuffer = new byte[m_nWidth * m_nHeigh];
m_byColorBuffer = new byte[m_nWidth * m_nHeigh * 3];
if (m_objIGXDevice.GetRemoteFeatureControl().IsImplemented("PixelColorFilter"))
{
strValue = m_objIGXDevice.GetRemoteFeatureControl().GetEnumFeature("PixelColorFilter").GetValue();
if ("None" != strValue)
{
m_bIsColor = true;
}
}
}
if (null != m_pic_ShowImage)
{
Bitmap bitmap = m_pic_ShowImage.Image as Bitmap;
m_pic_ShowImage.Image = m_bitmap;
if (bitmap != null)
{
bitmap.Dispose();
}
}
}
/// <summary>
///禁用窗体的关闭按钮只是ControlBox=false,会连同最小化和最大化按钮都不显示,所以,如果你想
///只想让关闭按钮不起作用,然后保留最小化、最大化的话,就重写窗体的CreateParams方法:
/// </summary>
protected override CreateParams CreateParams
{
get
{
CreateParams objCP = base.CreateParams;
objCP.ClassStyle = objCP.ClassStyle | CP_NOCLOSE_BUTTON;
return objCP;
}
}
/// <summary>
/// 单独设备的打开操作
/// </summary>
public void OpenDevice()
{
//打开流
if (null != m_objIGXDevice)
{
m_objIGXStream = m_objIGXDevice.OpenStream(0);
m_objIGXFeatureControl = m_objIGXDevice.GetRemoteFeatureControl();
m_objGxBitmap = new GxBitmap(m_objIGXDevice, m_pic_ShowImage);
}
}
/// <summary>
/// 开始采集
/// </summary>
public void StartDevice()
{
//开启采集流通道
if (null != m_objIGXStream)
{
//RegisterCaptureCallback第一个参数属于用户自定参数(类型必须为引用
//类型),若用户想用这个参数可以在委托函数中进行使用
m_objIGXStream.RegisterCaptureCallback(this, __OnFrameCallbackFun);
m_objIGXStream.StartGrab();
m_timer_UpdateFPS.Start();
}
//发送开采命令
if (null != m_objIGXFeatureControl)
{
m_objIGXFeatureControl.GetCommandFeature("AcquisitionStart").Execute();
}
}
/// <summary>
/// 停止采集
/// </summary>
public void StopDevice()
{
m_timer_UpdateFPS.Stop();
//发送停采命令
if (null != m_objIGXFeatureControl)
{
m_objIGXFeatureControl.GetCommandFeature("AcquisitionStop").Execute();
}
//关闭采集流通道
if (null != m_objIGXStream)
{
m_objIGXStream.StopGrab();
//注销采集回调函数
m_objIGXStream.UnregisterCaptureCallback();
}
}
/// <summary>
/// 关闭设备接口
/// </summary>
public void CloseDevice()
{
try
{
m_timer_UpdateFPS.Stop();
// 如果未停采则先停止采集
if (null != m_objIGXFeatureControl)
{
m_objIGXFeatureControl.GetCommandFeature("AcquisitionStop").Execute();
m_objIGXFeatureControl = null;
}
}
catch (Exception)
{
}
try
{
//停止流通道、注销采集回调和关闭流
if (null != m_objIGXStream)
{
m_objIGXStream.StopGrab();
//注销采集回调函数
m_objIGXStream.UnregisterCaptureCallback();
m_objIGXStream.Close();
m_objIGXStream = null;
}
}
catch (Exception)
{
}
try
{
//关闭设备
if (null != m_objIGXDevice)
{
m_objIGXDevice.Close();
m_objIGXDevice = null;
this.Close();
}
}
catch (Exception)
{
}
}
/// <summary>
/// 更新图像数据
/// </summary>
/// <param name="objIBaseData">图像数据对象</param>
void __UpdateImageData(IBaseData objIBaseData)
{
try
{
GX_VALID_BIT_LIST emValidBits = GX_VALID_BIT_LIST.GX_BIT_0_7;
if (null != objIBaseData)
{
emValidBits = __GetBestValudBit(objIBaseData.GetPixelFormat());
if (GX_FRAME_STATUS_LIST.GX_FRAME_STATUS_SUCCESS == objIBaseData.GetStatus())
{
if (m_bIsColor)
{
IntPtr pBufferColor = objIBaseData.ConvertToRGB24(emValidBits, GX_BAYER_CONVERT_TYPE_LIST.GX_RAW2RGB_NEIGHBOUR, true);
Marshal.Copy(pBufferColor, m_byColorBuffer, 0, m_nWidth * m_nHeigh * 3);
}
else
{
IntPtr pBufferMono = IntPtr.Zero;
if (__IsPixelFormat8(objIBaseData.GetPixelFormat()))
{
pBufferMono = objIBaseData.GetBuffer();
}
else
{
pBufferMono = objIBaseData.ConvertToRaw8(emValidBits);
}
Marshal.Copy(pBufferMono, m_byMonoBuffer, 0, m_nWidth * m_nHeigh);
}
}
}
}
catch (Exception)
{
}
}
/// <summary>
/// 回调函数,用于获取图像信息和显示图像
/// </summary>
/// <param name="obj">用户自定义传入参数</param>
/// <param name="objIFrameData">图像信息对象</param>
private void __OnFrameCallbackFun(object objUserParam, IFrameData objIFrameData)
{
try
{
lock (this)
{
m_objCFps.IncreaseFrameNum();
m_objGxBitmap.Show(objIFrameData);
}
}
catch (Exception)
{
}
}
/// <summary>
/// 更新界面的帧率显示
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void m_timer_UpdateFPS_Tick(object sender, EventArgs e)
{
m_objCFps.UpdateFps();
string strSN = string.Format("序号:{0} SN:{1}",m_nOperateID.ToString(), m_strSN) + " FPS:";
string strText = strSN + m_objCFps.GetFps().ToString("F2");
this.Text = strText;
}
/// <summary>
/// 判断PixelFormat是否为8位
/// </summary>
/// <param name="emPixelFormatEntry">图像数据格式</param>
/// <returns>true为8为数据,false为非8位数据</returns>
private bool __IsPixelFormat8(GX_PIXEL_FORMAT_ENTRY emPixelFormatEntry)
{
bool bIsPixelFormat8 = false;
uint uiPixelFormatEntry = (uint)emPixelFormatEntry;
if ((uiPixelFormatEntry & PIXEL_FORMATE_BIT) == GX_PIXEL_8BIT)
{
bIsPixelFormat8 = true;
}
return bIsPixelFormat8;
}
/// <summary>
/// 通过GX_PIXEL_FORMAT_ENTRY获取最优Bit位
/// </summary>
/// <param name="em">图像数据格式</param>
/// <returns>最优Bit位</returns>
private GX_VALID_BIT_LIST __GetBestValudBit(GX_PIXEL_FORMAT_ENTRY emPixelFormatEntry)
{
GX_VALID_BIT_LIST emValidBits = GX_VALID_BIT_LIST.GX_BIT_0_7;
switch (emPixelFormatEntry)
{
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO8:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR8:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG8:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB8:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG8:
{
emValidBits = GX_VALID_BIT_LIST.GX_BIT_0_7;
break;
}
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO10:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR10:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG10:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB10:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG10:
{
emValidBits = GX_VALID_BIT_LIST.GX_BIT_2_9;
break;
}
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO12:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR12:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG12:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB12:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG12:
{
emValidBits = GX_VALID_BIT_LIST.GX_BIT_4_11;
break;
}
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO14:
{
//暂时没有这样的数据格式待升级
break;
}
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_MONO16:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GR16:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_RG16:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_GB16:
case GX_PIXEL_FORMAT_ENTRY.GX_PIXEL_FORMAT_BAYER_BG16:
{
//暂时没有这样的数据格式待升级
break;
}
default:
break;
}
return emValidBits;
}
/// <summary>
/// 窗体和图像显示控件保持一致
/// </summary>
/// <param name="e"></param>
protected override void OnResize(EventArgs e)
{
base.OnResizeEnd(e);
m_pic_ShowImage.Width = this.Width;
m_pic_ShowImage.Height = this.Height;
}
private void m_pic_ShowImage_Click(object sender, EventArgs e)
{
}
private void ImageShowFrom_Load(object sender, EventArgs e)
{
}
}
}
相机开发中要注意的是:
如果进程任务很重,比如在进程中反复刷新UI界面,那么帧率就难以通过1/曝光率来估计了,而会收到计算机性能和进程竞争的制约,所以采集帧率保持高速采集,但是限制帧率,只每十帧显示一帧即可,降低进程负担。不能每采集一帧都显示一帧,这样进程压力会很大!