异步调用机制及实现方法

异步调用机制及实现方法
异步调用机制及实现方法

这篇文章将介绍异步调用的实现机制及如何调用异步方法。大多数.NET开发者在经过delegate、Thread、AsynchronousInvocation之后,通常都会对以上概念产生混淆及误用。实际上,以上概念是.NET2.0版本中对并行编程的核心支持,基于概念上的错误认识有可能导致在实际的编程中,无法利用异步调用的特性优化我们的程序,例如大数据量加载引起的窗体”假死”。事实上这并不是一个困难的问题,该文将以一种逐层深入、抽丝剥茧的方式逐渐深入到异步编程的学习中。

同步与异步

大多数人并不喜欢阅读大量的文字说明,而喜欢直接阅读代码,因此,我们在下文中将主要以代码的形式阐述同步与异步的调用。

同步方法调用

假设我们有一个函数,它的功能是将当前线程挂起3秒钟。

static void Sleep()

{

Thread.Sleep(3000);

}

通常,当你的程序在调用Sleep后,它将等待3秒钟的时间,在这3秒钟时间内,你不能做任何其他操作。3秒之后,控制权被交回给调用线程(通常也就是你的主线程,即WinForm程序的UI线程)。这种类型的调用称为同步,本次调用顺序如下:

● 调用Sleep();

● Sleep()执行中;

● Sleep()执行完毕,控制权归还调用线程。

我们再次调用Sleep()函数,不同的是,我们要基于委托来完成这次调用。一般为了将函数绑定在委托中,我们要定义与函数返回类型、参数值完全一致的委托,这稍有点麻烦。但.NET内部已经为我们定义好了一些委托,例如MethodInvoker,这是一种无返回值、无参数的委托签名,这相当于你自定义了一种委托:

public delegate void SimpleHandler();

执行以下代码:

MethodInvoker invoker = new MethodInvoker(Sleep);

invoker.Invoke();

我们使用了委托,但依然是同步的方式。主线程仍然要等待3秒的挂起,然后得到响应。

注意:Delegate.Invoke是同步方式的。

异步方法调用

如何在调用Sleep()方法的同时,使主线程可以不必等待Sleep()的完成,一直能够得到相应呢?这很重要,它意味着在函数执行的同时,主线程依然是非阻塞状态。在后台服务类型的程序中,非阻塞的状态意味着该应用服务可以在等待一项任务的同时去接受另一项任务;在传统的WinForm程序中,意味着主线程(即UI线程)依然可以对用户的操作得到响应,避免了”假死”。我们继续调用Sleep()函数,但这次要引入BeginInvoke。

MethodInvoker invoker = new MethodInvoker(Sleep);

invoker.BeginInvoke(null, null);

● 注意BeginInvoke这行代码,它会执行委托所调用的函数体。同时,调用BeginInvoke方法的线程(以下简称为调用线程)会立即得到响应,而不必等待Sleep()函数的完成。

● 以上代码是异步的,调用线程完全可以在调用函数的同时处理其他工作,但是不足的是我们仍然不知道对于Sleep()函数的调用何时会结束,这是下文将要解决的问题。

● eginInvoke可以以异步的方式完全取代Invoke,我们也不必担心函数包含参数的情况,下文介绍传值问题。

注意:Delegate.BeginInvoke是异步方式的。如果你要执行一项任务,但并不关心它何时完成,我们就可以使用BeginInvoke,它不会带来调用线程的阻塞。

对于异步调用,.NET内部究竟做了什么?

一旦你使用.NET完成了一次异步调用,它都需要一个线程来处理异步工作内容(以下简称异步线程),异步线程不可能是当前的调用线程,因为那样仍然会造成调用线程的阻塞,与同步无异。事实上,.NET

会将所有的异步请求队列加入线程池,以线程池内的线程处理所有的异步请求。对于线程池似乎不必了解的过于深入,但我们仍需要关注以下几点内容:

● Sleep()的异步调用会在一个单独的线程内执行,这个线程来自于.NET线程池。

● .NET线程池默认包含25个线程,你可以改变这个值的上限,每次异步调用都会使用其中某个线程执行,但我们并不能控制具体使用哪一个线程。

● 线程池具备最大线程数目上限,一旦所有的线程都处于忙碌状态,那么新的异步调用将会被置于等待队列,直到线程池产生了新的可用线程,因此对于大量异步请求,我们有必要关注请求数量,否则可能造成性能上的影响。

简单了解线程池

为了暴露线程池的上限,我们修改Sleep()函数,将线程挂起的时间延长至30s。在代码的运行输出结果中,我们需要关注以下内容:

● 线程池内的可用线程数量。

● 异步线程是否来自于线程池。

● 线程托管ID值。

上文已经提到,.NET线程池默认包含25个线程,因此我们连续调用30次异步方法,这样可以在第25次调用后,看看线程池内部究竟发生了什么。

private void Sleep()

{

int intAvailableThreads, intAvailableIoAsynThreds;

// 取得线程池内的可用线程数目,我们只关心第一个参数即可

ThreadPool.GetAvailableThreads(out intAvailableThreads,

out intAvailableIoAsynThreds);

// 线程信息

string strMessage =

String.Format("是否是线程池线程:{0},线程托管ID:{1},可用线程数:{2}",

Thread.CurrentThread.IsThreadPoolThread.ToString(),

Thread.CurrentThread.GetHashCode(),

intAvailableThreads);

Console.WriteLine(strMessage);

Thread.Sleep(30000);

}

private void CallAsyncSleep30Times()

{

// 创建包含Sleep函数的委托对象

MethodInvoker invoker = new MethodInvoker(Sleep);

for (int i = 0; i < 30; i++)

{

// 以异步的形式,调用Sleep函数30次

invoker.BeginInvoke(null, null);

}

}

输出结果:

对于输出结果,我们可以总结为以下内容:

● 所有的异步线程都来自于.NET线程池。

● 每次执行一次异步调用,便产生一个新的线程;同时可用线程数目减少。

● 在执行异步调用25次后,线程池中不再有空闲线程。此时,应用程序会等待空闲线程的产生。

● 一旦线程池内产生了空闲线程,它会立即被分配给异步任务等待队列,之后线程池中仍然不具备空闲线程,应用程序主线程进入挂起状态继续等待空闲线程,这样的调用一直持续到异步调用被执行完30次。

针对以上结果,我们对于异步调用可以总结为以下内容:

● 每次异步调用都在新的线程中执行,这个线程来自于.NET线程池。

● 线程池有自己的执行上限,如果你想要执行多次耗费时间较长的异步调用,那么线程池有可能进入一种”线程饥饿”状态,去等待可用线程的产生。

BeginInvoke和EndInvoke

我们已经知道,如何在不阻塞调用线程的情况下执行一个异步调用,但我们无法得知异步调用的执行结果,及它何时执行完毕。为了解决以上问题,我们可以使用EndInvoke。EndInvoke在异步方法执行完成前,都会造成线程的阻塞。因此,在调用BeginInvoke之后调用EndInvoke,效果几乎完全等同于以阻塞模式执行你的函数(EndInvoke会使调用线程挂起,一直到异步函数执行完毕)。但是,.NET是如何将BeginInvoke和EndInvoke进行绑定呢?答案就是IAsyncResult。每次我们使用BeginInvoke,返回值都是IAsyncResult类型,它是.NET追踪异步调用的关键值。每次异步调用之后的结果如何?如果要了解具体执行结果,IAsyncResult便可视为一个标签。通过这个标签,你可以了解异步调用何时执行完毕,更重要的是,它可以保存异步调用的参数传值,解决异步函数上下文问题。

我们现在通过几个例子来了解IAsyncResult。如果之前对它了解不多,那么就需要耐心的将它领悟,因为这种类型的调用是.NET异步调用的关键内容。

private void SleepOneSecond()

{

// 当前线程挂起1秒

Thread.Sleep(1000);

}

private void UsingEndInvoke()

{

// 创建一个指向SleepOneSecond的委托

MethodInvoker invoker = new MethodInvoker(SleepOneSecond);

// 开始执行SleepOneSecond,但这次异步调用我们传递一些参数

// 观察Delegate.BeginInvoke()的第二个参数

IAsyncResult tag = invoker.BeginInvoke(null, "passing som e state");

// 应用程序在此处会造成阻塞,直到SleepOneSecond执行完成

invoker.EndInvoke(tag);

// EndInvoke执行完毕,取得之前传递的参数内容

string strState = (string)tag.AsyncState;

Console.WriteLine("EndInvoke的传递参数" + tag.AsyncState.ToString());

}

输出结果:

回到文章初始提到的”窗体动态更新”问题,如果你将上述代码运行在一个WinForm程序中,会发现窗体依然陷入”假死”。对于这种情况,你可能会陷入疑惑:之前说异步函数都执行在线程池中,因此可以肯定异步函数的执行不会引起UI线程的忙碌,但为什么窗体依然陷入了”假死”?问题就在于EndInvoke。EndInvoke此时扮演的角色就是”线程锁”,它充当了一个调用线程与异步线程之间的调度器,有时调用线程需要使用异步函数的执行结果,那么调度线程就需要在异步执行完之前一直等待,直到得到结果方可继续运行。EndInvoke一方面负责监听异步函数的执行状况,一方面将调用线程挂起。

因此在Win Form环境下,UI线程的”假死”并不是因为线程忙碌造成,而是被EndInvoke”善意的”暂时封锁,它只是为了等待异步函数的完成。

我们可以对EndInvoke总结如下:

● 在执行EndInvoke时,调用线程会进入挂起状态,一直到异步函数执行完成。

● 使用EndInvoke可以使应用程序得知异步函数何时执行完毕。

● 如果将上述写法称为”异步”,你一定觉得这种”异步”徒具其名,虽然知道异步函数何时执行完毕,也得到了异步函数的传值,但我们的调用线程仍然会等待函数执行完毕,在等待过程中线程阻塞,实际上与同步调用无异。

如何捕捉异常?

现在我们把问题稍微复杂化,考虑异步函数抛出异常的一种情形。我们需要了解在何处捕捉到异常,是BeginInvoke,还是EndInvoke?甚至是有没有可能无法捕捉异常?答案是EndInvoke。BeginInvoke的工作只是开始线程池对于异步函数的执行工作,EndInvoke则需要处理函数执行完成的所有信息,包括其中产生的异常。

private void SleepOneSecond()

{

Thread.Sleep(3000);

throw new Exception("Here Is An Async Function Exception");

}

private void UsingEndInvoke()

{

// 创建一个指向SleepOneSecond的委托

MethodInvoker invoker = new MethodInvoker(SleepOneSecond);

// 开始执行SleepOneSecond,但这次异步调用我们传递一些参数

// 观察Delegate.BeginInvoke()的第二个参数

IAsyncResult tag = invoker.BeginInvoke(null, "passing som e state");

try

{

// 应用程序在此处会造成阻塞,直到SleepOneSecond执行完成

invoker.EndInvoke(tag);

}

catch (Exception ex)

{

// 此处可以捕捉异常

MessageBox.Show(ex.Message);

}

// EndInvoke执行完毕,取得之前传递的参数内容

string strState = (string)tag.AsyncState;

Console.WriteLine("EndInvoke的传递参数" + tag.AsyncState.ToString());

}

执行以上代码后,你将发现只有在使用EndInvoke时,才会捕捉到异常,否则异常将丢失。需要注意的是,直接在编译器中运行程序是无法产生捕获异常的,只有在Debug、Release环境下运行,异常才会以对话框的形式直接弹出。

向函数中传递参数

现在我们来改变一下异步函数,让它接收一些参数。

private string FuncWithParam eters(int param1, string param2, ArrayList param3)

{

// 我们在这里改变参数值

param1 = 100;

param2 = "hello";

param3 = new ArrayList();

return "thank you for reading me";

}

下面我们使用BeginInvoke与EndInvoke来调用这个函数,首先,我们创建一个匹配该函数的委托签名。

public delegate string DelegateWithParameters(int param1, string param2, ArrayList param3);

我们可以将BeginInvoke和EndInvoke视为将异步函数分割为两部分的特殊函数。BeginInvoke 通过自己的两个参数值(一个AsyncCallBack委托,一个object对象)来接收传入参数,EndInvoke用于计算传出参数(标记了out或者ref的参数)和函数返回值。

现在我们回到自己的函数FuncWithParameters,param1、param2、param3是传入值,同时,它们也作为BeginInvoke的参数来处理;函数的返回值是string类型,它将作为EndInvoke的返回类型。比较酷的是,编译器可以通过委托类型,来自动为BeginInvoke和EndInvoke生成正确的参数与返回值类型。

注意我们在异步函数中为参数分配了新的值,这样可以检验这些参数在调用异步函数后,究竟会传出什么样的值……

private void CallFuncWithParam eters()

{

// 创建几个参数

string strParam = "Param1";

int intValue = 100;

ArrayList list = new ArrayList();

list.Add("Item1");

// 创建委托对象

DelegateWithParameters delFoo =

new DelegateWithParameters(FuncWithParam eters);

// 调用异步函数

IAsyncResult tag =

delFoo.BeginInvoke(intValue, strParam, list, null, null);

// 通常调用线程会立即得到响应

// 因此你可以在这里进行一些其他处理

// 执行EndInvoke来取得返回值

string strResult = delFoo.EndInvoke(tag);

Trace.WriteLine("param1: " + intValue);

Trace.WriteLine("param2: " + strParam);

Trace.WriteLine("ArrayList count: " + list.Count);

}

我们的异步函数对参数的改变并没有影响其传出值,现在我们把ArrayList变为ref参数,看看会给EndInvoke带来什么变化。

public delegate string DelegateWithParameters(out int param1, string param2, ref ArrayList param3);

private string FuncWithParam eters(out int param1, string param2, ref ArrayList param3) {

// 我们在这里改变参数值

param1 = 300;

param2 = "hello";

param3 = new ArrayList();

return "thank you for reading me";

}

private void CallFuncWithParam eters()

{

// 创建几个参数

string strParam = "Param1";

int intValue = 100;

ArrayList list = new ArrayList();

list.Add("Item1");

// 创建委托对象

DelegateWithParameters delFoo =

new DelegateWithParameters(FuncWithParam eters);

// 调用异步函数

IAsyncResult tag =

delFoo.BeginInvoke(out intValue, strParam, ref list, null, null);

// 通常调用线程会立即得到响应

// 因此你可以在这里进行一些其他处理

// 调用EndInvoke,发现intValue和list可以作为参数被传出,

// 是因为他们可以被异步函数更新

string strResult = delFoo.EndInvoke(out intValue, ref list, null);

Trace.WriteLine("param1: " + intValue);

Trace.WriteLine("param2: " + strParam);

Trace.WriteLine("ArrayList count: " + list.Count);

}

param2没有变化,因为它是输入参数;param1作为输出参数,被更新为300;ArrayList的值已被重新分配,我们可以发现它的引用被指向了一个空元素的ArrayList对象(初始引用已丢失)。通过以上实例,我们应该能理解参数是如何在BeginInvoke与EndInvoke之间传递的。现在我们来尝试完成一个非阻塞模式下的异步调用,这是个重头戏!

同步传输与异步传输的区别

同步传输与异步传输的区别 数据块与数据块之间的时间间隔是固定的,必须严格地规定它们的时 列,标记一个数据块的开始和结束,一般还要附加一个校验序列,以 同步传输的特点:同步传输的比特分组要大得多。它不是独立地 异步传输是数据传输的一种方式。由于数据一般是一位接一位串行传输的,例如在传送一串字符信息时,每个字符代码由7位二进制位组成。但在一串二进制位中,每个7位又从哪一个二进制位开始算起呢?异步传输时,在传送每个数据字符之前,先发送一个叫做开始位的二进制位。当接收端收到这一信号时,就知道相继送来7位二进制位是一个字符数据。在这以后,接着再给出1位或2位二进制位,称做结束位。接收端收到结束位后,表示一个数据字符传送结束。这样,在异步传输时,每个字符是分别同步的,即字符中的每个二进制位是同步的,但字符与字符之间的间隙长度是不固定的。 异步传输的特点:将比特分成小组进行传送,小组可以是8位的 从不知道它们会在什么时候到达。一个常见的例子是计算机键盘与主

异步传输,英文名AsynchronousTransfer Mode,ATM,是实现B-ISDN的一项技术基础,是建立在电路交换和分组交换的基础上的快速分组交换技术。ATM的主要特点是面向连接;采用小的、固定长度的单元(53字节);取消链路的差错控制和流量控制等,这些措施提高了传输效率。。ATM 的突出优点是可以为每个虚连接提供相应的服务质量(QOS),可以有效地支持视、音频多媒体传输,包括语音、视频和数据等;另外,ATM可以实现局域网和广域网的平滑无缝连接。 [2] 异步传输一般以字符为单位,不论所采用的字符代码长度为多少位,在发送每一 异步传输 字符代码时,前面均加上一个“起”信号,其长度规定为1个码元,极性为“0”,即空号的极性;字符代码后面均加上一个“止”信号,其长度为1或者2个码元,极性皆为“1”,即与信号极性相同,加上起、止信号的作用就是为了能区分串行传输的“字符”,也就是实现了串行传输收、发双方码组或字符的同步。 综上所述,同步传输与异步传输的简单区别:1、异步传输是面向字符的传输,而同步传输是面向比特的传输。 2,异步传输的单位是字符,而同步传输的单位是帧。

C++ 串口API 异步操作

C++ 串口API 异步操作50 有谁能说下串口API异步操作的例子,最好有代码的。 如果是BCB的更好,谢谢啦,有些函数直接看的不是很懂。一,我要同时向20个端口发送数据,句柄怎样才能控制好二,如果采用异步操作,怎样操作才最好,需要代码支持谢谢啦初始化: //串行设备句柄; HANDLE hComDev=0; //串口打开标志; BOOL bOpen=FALSE; //线程同步事件句柄; HANDLE hEvent=0; DCB dcb; COMMTIMEOUTS timeouts; //设备已打开 if(bOpen) return FALSE; //打开COM1

if((hComDev=CreateFile(“COM1”,GENERIC?READ|GENERIC?WRITE,0,N ULL,OPEN?EXISTING,FILE?ATTRIBUTE?NORMAL,NULL))==INVALID?HAN DLE?VALUE) return FALSE; //设置超时控制 SetCommTimeouts(hComDev,&timeouts); //设置接收缓冲区和输出缓冲区的大小 SetupComm(hComDev,1024,512); //获取缺省的DCB结构的值 GetCommState(hComDev,&dcb); //设定波特率为9600 bps dcb.BaudRate=CBR?9600; //设定无奇偶校验 dcb.fParity=NOPARITY; //设定数据位为8 dcb.ByteSize=8;

//设定一个停止位 dcb.StopBits=ONESTOPBIT; //监视串口的错误和接收到字符两种事件SetCommMask(hComDev,EV?ERR|EV?RXCHAR); //设置串行设备控制参数 SetCommState(hComDev,&dcb); //设备已打开 bOpen=TRUE; //创建人工重设、未发信号的事件 hEvent=CreateEvent(NULL,FALSE,FALSE, “WatchEvent”);

如何确保实现目标

如何确保实现目标 教学目标 本课程主要讲述了目标执行的整个过程,包括确定执行标准、责任归属、过程控制、目标跟踪、目标检查和目标修正,可以让学员掌握目标实现过程中的重要环节,从而信心百倍地去实现设定的目标。课程收益 学习本课程后,学员可以知道目标执行的标准和责任,学会目标控制,掌握目标跟踪和检查的技巧,并能够对出现偏差的目标进行修正。 课程提纲 一、确保目标的执行 二、目标执行中的控制 三、跟踪目标 四、检查目标 五、修正目标 一、确保目标的执行 1、制定目标执行标准

2、明确目标责任 (1)明确目标责任的原则 (2)明确责任的方法 (3)专人督导目标的执行 (4)目标执行阶段,组织内所有成员应注意的事项 二、目标执行中的控制 1、目标控制的3个步骤 建立控制标准衡量实施成效纠正偏差措施 2、目标控制的要领 (1)能够高瞻远瞩,多做预测和估计; (2)能够反映出行动的性质和基本要求; (3)够迅速觉察出实施与目标间的差异; (4)能够把握关键点; (5)要以适当的标准为前提; (6)要有适度的弹性; (7)必须合乎经济的原则; (8)要表现出组织的效能; (9)方法与技术要做到易于了解; (10)应能够指出要改正的行动。 3、目标控制的方法 (1)一般意义上的控制

(2)计划控制 三、跟踪目标 1、跟踪的目的和原则 (1)跟踪的目的 利用考核手段激发员工的责任意识;发现目标执行过程中 存在偏差,可以及时、适时地修正;提供上级与下属间定 期的正式沟通机会 (2)跟踪的原则 2、如何进行有效的工作跟踪 (1)工作跟踪的要求 (2)工作跟踪的步骤 (3)实行跟踪检查需注意的事项 (4)在工作追踪中出现的问题 四、检查目标 1、检查目标的内容和方法 (1)检查目标的内容 进度检查;质量检查;均衡检查;协作检查;对策检查 (2)检查目标的方法 2、建立检查制度的注意点 (1)目标检查的深度 (2)目标检查的要领 五、修正目标

基于Struts2 Result Type为chain 的Action之间数据传递

chain:基本用途是构造成一条动作链。前一个Action将控制权转交给后一个Action,而前一个Action的状态在后一个Action里仍然保持着。 我现在有一个场景,FirstAction 通过chain的方式,将控制权交给SecondAction。FirstAction对应的页面代码为first.ftl,SecondAction对应的页面代码为second.ftl。 假设我们的FirstAction如下定义: public class SecondAction extends ActionSupport{ private CustomUser user = null; public String execute() throws Exception { // 利用user做事情或显示在页面上 } // getter setter } 意思很明确了,通过first.ftl的输入,到DB中或其他,生成了我们的CustomUser对象,这个CustomUser对象将要在SecondAction使用。 于是我们想到了要配置FirstAction 的name为toSecond的Result type为chain,将生成的CustomUser对象传递到SecondAction中, 我们也这样做了,但是经过调试,发现在SecondAction中没有得到FirstAction中的CustomUser对象。 SecondAction是这样实现的: public class SecondAction extends ActionSupport{ private CustomUser user = null; public String execute() throws Exception { // 利用user做事情或显示在页面上 } // getter setter } 看一下ChainingInterceptor.java的实现,发现有这样的注释: An interceptor that copies all the properties of every object in the value stack to t he currently executing object.

AJAX案例

div 部分用于显示来自服务器的信息。当按钮被点击时,它负责调用名为loadXMLDoc() 的函数:

Let AJAX change this text

接下来,在页面的 head 部分添加一个 AJAX - 创建 XMLHttpRequest 对象?Previous Page ?Next Page XMLHttpRequest 是 AJAX 的基础。 XMLHttpRequest 对象 所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用ActiveXObject)。 XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。 创建 XMLHttpRequest 对象

所有现代浏览器(IE7+、Firefox、Chrome、Safari 以及 Opera)均内建XMLHttpRequest 对象。 创建 XMLHttpRequest 对象的语法: variable=new XMLHttpRequest(); 老版本的 Internet Explorer (IE5 和 IE6)使用 ActiveX 对象: variable=new ActiveXObject("Microsoft.XMLHTTP"); 为了应对所有的现代浏览器,包括 IE5 和 IE6,请检查浏览器是否支持XMLHttpRequest 对象。如果支持,则创建 XMLHttpRequest 对象。如果不支持,则创建 ActiveXObject : var xmlhttp; if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp=new XMLHttpRequest(); } else {// code for IE6, IE5 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } 在下一章中,您将学习发送服务器请求的知识。 AJAX - 向服务器发送请求 ?Previous Page ?Next Page XMLHttpRequest 对象用于和服务器交换数据。 向服务器发送请求 如需将请求发送到服务器,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法: xmlhttp.open("GET","test1.txt",true); xmlhttp.send();

同步复位和异步复位的区别

针对数字系统的设计,我们经常会遇到复位电路的设计,对初学者来说不知道同步复位与异步复位的区别与联系,今天我对这个问题简要的阐述下,希望对初学者有一定的参考意义,若有不正确的地方愿大家明示。 同步复位原理:同步复位只有在时钟沿到来时复位信号才起作用,则复位信号持续的时间应该超过一个时钟周期才能保证系统复位。 异步复位原理:异步复位只要有复位信号系统马上复位,因此异步复位抗干扰能力差,有些噪声也能使系统复位,因此有时候显得不够稳定,要想设计一个好的复位最好使用异步复位同步释放。 同步复位与异步复位的优劣:异步复位消耗的PFGA逻辑资源相对来说要少些,因此触发器自身带有清零端口不需要额外的门电路,这是其自身的优势,通常在要求不高的情况下直接使用异步复位就OK。 下面我用verilog来演示下同步复位与异步复位。 同步复位的verilog程序如下: module D_FF (

//Input ports SYSCLK, RST_B, A, //Output ports B ); //========================================= //Input and output declaration //========================================= input SYSCLK; input RST_B; input A; output B; //========================================= //Wire and reg declaration //=========================================

UART异步串口

MSP430程序库<二>UART异步串口 串行通信接口是处理器与其他设备进行数据通信最常用的方式之一。我的这个程序库是针对MSP430f14系列和MSP430f16系列的,我常用的单片机是这两款:msp430f149,ms p430f169。这两款单片机中均有两个增强型串行通信接口,都可以进行同步或是异步通信,甚至169的模块USART0还能进行进行I2C协议通信。在这里,我们只讨论异步串行通信。 硬件介绍: MSP单片机的USART模块可以配置成SPI(同步通信)模式或UART(异步通信)模式,这里只讨论UART方式。UART数据传输格式如下: 起始位,数据位由高到低7/8位,地址位0/1位,奇偶校验位奇偶或无,停止位1/2位。数据位位数、地址位、奇偶校验位、停止位均可由单片机内部寄存器控制;这两款单片机都有两个USART模块,有两套独立的寄存器组;以下寄存器命中出现x代表0或是1,0代表对应0模块的寄存器,1代表对应1模块的寄存器;其中,与串口模式设置相关的控制位都位于UxCTL寄存器,与接收相关的控制位都位于UxRCTL寄存器,与发送相关的控制位都位于UxTCTL寄存器;波特率设置用UxBR0、UxBR1、UxMCT L三个寄存器;接收与发送有独立的缓存UxRXBUF、UxTXBUF,并具有独立的移位寄存器和独立的中断;中断允许控制位位于IE1/2寄存器,中断标志位位于IFG1/2寄存器。 波特率设置:430的波特率设置用三个寄存器实现, UxBR0:波特率发生器分频系数低8位。 UxBR1:波特率发生器分频系数高8位。 UxMCTL:波特率发生器分频系数的小数部分实现。 设置波特率时,首先要选择合适的时钟源:USART模块可以设置的时钟源有UCLK引脚、ACLK、SMCLK;对于较低的波特率(9600以下),可选ACLK作为时钟源,这样,在LPM3(低功耗3)模式下,串口仍能正常发送接收数据;另外,由于串口接收过程有一个三取二判决逻辑,这至少需要三个时钟周期,因此分频系数必须大于3;波特率高于9600时,将不能使用ACLK作为时钟源,要调为频率较高的SMCLK作为时钟源;另外还可以外部输入UCLK时钟。分频系数计算公式如下:

JAVA之struts2+ajax实现echats柱状图

说明:本实例使用的是echart3,jquery1.8.1 1)struts的web.xml配置代码如下: struts2 index.html index.htm index.jsp default.html default.htm default.jsp struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecu teFilter struts2 /* 2)struts.xml代码 abc console.log(https://www.360docs.net/doc/269879220.html,); // --> abc o2.age = '123';console.log(o1.age); // --> 123console.log(o2.age); // --> 123 JS中的常见对置对象类

同步传输与异步传输的区别

在网络通信过程中,通信双方要交换数据,需要高度的协同工作。为了正确的解释信号,接收方必须确切地知道信号应当何时接收和处理,因此定时是至关重要的。在计算机网络中,定时的因素称为位同步。同步是要接收方按照发送方发送的每个位的起止时刻和速率来接收数据,否则会产生误差。通常可以采用同步或异步的传输方式对位进行同步处理。 1. 异步传输(Asynchronous Transmission):异步传输将比特分成小组进行传送,小组可以是8位的1个字符或更长。发送方可以在任何时刻发送这些比特组,而接收方从不知道它们会在什么时候到达。一个常见的例子是计算机键盘与主机的通信。按下一个字母键、数字键或特殊字符键,就发送一个8比特位的ASCII代码。键盘可以在任何时刻发送代码,这取决于用户的输入速度,内部的硬件必须能够在任何时刻接收一个键入的字符。 异步传输存在一个潜在的问题,即接收方并不知道数据会在什么时候到达。在它检测到数据并做出响应之前,第一个比特已经过去了。这就像有人出乎意料地从后面走上来跟你说话,而你没来得及反应过来,漏掉了最前面的几个词。因此,每次异步传输的信息都以一个起始位开头,它通知接收方数据已经到达了,这就给了接收方响应、接收和缓存数据比特的时间;在传输结束时,一个停止位表示该次传输信息的终止。按照惯例,空闲(没有传送数据)的线路实际携带着一个代表二进制1的信号,异步传输的开始位使信号变成0,其他的比特位使信号随传输的数据信息而变化。最后,停止位使信号重新变回1,该信号一直保持到下一个开始位到达。例如在键盘上数字“1”,按照8比特位的扩展ASCII编码,将发送“00110001”,同时需要在8比特位的前面加一个起始位,后面一个停止位。 异步传输的实现比较容易,由于每个信息都加上了“同步”信息,因此计时的漂移不会产生大的积累,但却产生了较多的开销。在上面的例子,每8个比特要多传送两个比特,总的传输负载就增加25%。对于数据传输量很小的低速设备来说问题不大,但对于那些数据传输量很大的高速设备来说,25%的负载增值就相当严重了。因此,异步传输常用于低速设备。 2. 同步传输(Synchronous Transmission):同步传输的比特分组要大得多。它不是独立地发送每个字符,每个字符都有自己的开始位和停止位,而是把它们组合起来一起发送。我们将这些组合称为数据帧,或简称为帧。 数据帧的第一部分包含一组同步字符,它是一个独特的比特组合,类似于前面提到的起始位,用于通知接收方一个帧已经到达,但它同时还能确保接收方的采样速度和比特的到达速度保持一致,使收发双方进入同步。 帧的最后一部分是一个帧结束标记。与同步字符一样,它也是一个独特的比特串,类似于前面提到的停止位,用于表示在下一帧开始之前没有别的即将到达的数据了。

Win32API 异步串口通讯

使用Win32API实现Windows下异步串口通讯 目录: 1.异步非阻塞串口通讯的优点 2.异步非阻塞串口通讯的基本原理 3.异步非阻塞串口通讯的基础知识 4.异步非阻塞串口通讯的实现步骤 一,异步非阻塞串口通讯的优点 读写串行口时,既可以同步执行,也可以重叠(异步)执行。 在同步执行时,函数直到操作完成后才返回。这意味着在同步执行时线程会被阻塞,从而导致效率下降。在重叠执行时,即使操作还未完成,调用的函数也会立即返回。费时的I/O操作在后台进行,这样线程就可以干别的事情。 例如,线程可以在不同的句柄上同时执行I/O操作,甚至可以在同一句柄上同时进行读写操作。"重叠"一词的含义就在于此。 二,异步非阻塞串口通讯的基本原理 首先,确定要打开的串口名、波特率、奇偶校验方式、数据位、停止位,传递给CreateFile()函数打开特定串口; 其次,为了保护系统对串口的初始设置,调用GetCommTimeouts()得到串口的原始超时设置; 然后,初始化DCB对象,调用SetCommState() 设置DCB,调用SetCommTimeouts()设置串口超时控制;再次,调用SetupComm()设置串口接收发送数据的缓冲区大小,串口的设置就基本完成,之后就可以启动读写线程了。 三,异步非阻塞串口通讯的基础知识 下面来介绍并举例说明一下编写异步非阻塞串口通讯的程序中将会使用到的几个关键函数 CreateFile() 功能:打开串口设备 函数原型 HANDLE CreateFile( LPCTSTR lpFileName, // 串口名称字符串;如:"COM1" 或"COM2" DWORD dwDesiredAccess, // 设置读写属性(访问模式);一般为GENERIC_READ|GENERIC_WRITE, DWORD dwShareMode, // 共享模式;"必须"为0, 即不能共享 LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性;一般为NULL DWORD dwCreationDistribution, // 创建方式,串口设置必须设置此值;在这里"必须"为OPEN_EXISTING DWORD dwFlagsAndAttributes, // 文件属性和标志;在这里我们设置成FILE_FLAG_OVERLAPPED ,实现异步I/O HANDLE hTemplateFile // 临时文件的句柄,通常为NULL ); 说明: 如果调用成功,那么该函数返回文件的句柄,如果调用失败,则函数返回INVALID_HANDLE_VALUE。Forexample: Handle m_hComm = CreateFile(com1,GENERIC_READ||GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERL APPED,0);

要实现你的目标,你必须怎样做

关于成功,有各种各样的说法。那到底什么是成功?怎么样才算成功?以最贴近生活的说法,成功就是不断达成目标,成功就是每天进步一点点。这是“成功”本身的一个理念。 本文主要讲述了实现目标、迈向成功的正确理念和实现目标、有效提高时间使用效率的方法,是许多成功人士所具有的共同特征和行为习惯的综合性的概括和总结,值得我们借鉴和学习。特别是本文的第一部分,其主要内容是从本文的参考书《1%的改变就改变你的全部》归纳总结而来,该书作者是韩国著名的心理学博士、汉城大学教授李民奎,本文第一部分中讲述的主要理念和方法都是以心理学知识作为基础的。相信如果你能够深入领会本文精髓并付诸实践,你一样可以实现自己的目标。 本文结构上共分为五部分。第一部分讲述实现成功的核心理念及其框架,详细分析了各个核心理念的内涵、构成要素和各个理念之间的相互关系。它们的整体结构又形成了一种实现目标、迈向成功的有效工具和方法。第二部分讲述关于成功的最重要的一些理念和解决实际问题的一些有效方法。这部分内容对于你的学习和今后的工作具有很强的引导作用,因为正确的理念是成功的前提条件。第三部分是指导你怎么检查和分析你的现状,发现你的问题。这部分工作必不可少,因为分析你的现状,发现问题所在是实现目标、迈向成功的首要工作和先决条件。第四部分是本文的方法在数学科目具体运用的一个实例,有助于你对本文内容的更深刻的理解和领会。第五部分是本文自身的一些总结,包括概念和方法两方面的内容,可以帮助你从整体上理解和掌握本文的精髓。 第一部分核心理念框架 一. 核心理念框架 主题思想:本部分是实现理想和目标的流程示意图(过程图),是成功者走向成功的共同经验的总结。每一个步骤都不可少,熟记这个过程并切实付诸实践,你将可以少走弯路,更快地迈向成功。这个过程适用于你的学习、工作和生活中任何一个目标的实现,也适用于你人生中的任何一个阶段。本过程图同样可以作为人生规划或职业规划的工具。(本部分编写时间:2005年7月12日17:00-19:30,2005年7月13日6:00-8:00) 理想 ∣理想就是你想成为什么样的人?你将来要从事什么职业?理想要根据社会发 ∣展需要和个人兴趣结合考虑。人更容易在感兴趣的领域取得成绩。在制定目 ∣标之前仔细分析你现在所处的状况和现有的条件,明确努力的方向。如果有 ↓ 什么不满,就要寻找造成这种不满的原因,然后改变过去的思想和做法。

Struts2+JQuery+JSON实现AJAX

Struts2 + JQuery + JSON实现AJAX 网上关于这方面的资料也不少,但多半是struts1的,在Struts2中使用JSON 可以更容易实现数据的异步传输。 先做好准备工作: 1.Struts2相关lib, 注意将struts2 lib下面的以json开头的包也加入到工程。 2.JSON Plugin,它可以将Struts2 Action中的结果直接返回为JSON。下载地址: https://www.360docs.net/doc/269879220.html,/files/jsonplugin-0.34.jar(支持struts2.1.6及以上版本)。 3.JQuery,JS的一个lib. 下载地址:https://www.360docs.net/doc/269879220.html,(最新版本为1.3.2)。 准备工作都做好之后,我们可以开始了。建一个WEB工程,把相关的包加入到工程。我们需要做的就是三件事: 一、准备一个JSP页面用于提交ajax请求,这里我使用了JQuery的$.getJSON(url,params,function callback(data))函数提交ajax请求到指定url,并且携带参数params,最后用一个回调函数callback处理请求返回结果data; 二、一个处理请求的Action类,并在struts.xml文件中做相应配置:写一个action类处理ajax请求数据,并将返回结果封装成一个JSONObject对象返回给请求页面。同时在struts.xml中配置对应action,指明其返回类型为json 并使其package的extends为json-default,并将要返回请求页面的数据放在名为root的param中,如result。 三、接受请求返回结果:使用JS的eval方法将返回结果data转换成JSON 对象,并处理返回结果。 具体参见以下代码: // login.jsp 使用getJSON方法提交ajax请求,并处理请求返回结果。注意请求的url为login.html这是因为我将struts2.preperties中的 struts.action.extension改成了htm,默认为action。l Html代码

同步通信与异步通信区别

同步通信与异步通信区别 1.异步通信方式的特点:异步通信是按字符传输的。每传输一个字符就用起始位来进来收、发双方的同步。不会因收发双方的时钟频率的小的偏差导致错误。这种传输方式利用每一帧的起、止信号来建立发送与接收之间的同步。特点是:每帧内部各位均采用固定的时间间隔,而帧与帧之间的间隔时随即的。接收机完全靠每一帧的起始位和停止位来识别字符时正在进行传输还是传输结束。 2.同步通信方式的特点:进行数据传输时,发送和接收双方要保持完全的同步,因此,要求接收和发送设备必须使用同一时钟。优点是可以实现高速度、大容量的数据传送;缺点是要求发生时钟和接收时钟保持严格同步,同时硬件复杂。可以这样说,不管是异步通信还是同步通信都需要进行同步,只是异步通信通过传送字符内的起始位来进行同步,而同步通信采用共用外部时钟来进行同步。所以,可以说前者是自同步,后者是外同步。---------------------------- 同步通信原理 同步通信是一种连续串行传送数据的通信方式,一次通信只传送一帧信息。这里的信息帧与异步通信中的字符帧不

同,通常含有若干个数据字符。 采用同步通信时,将许多字符组成一个信息组,这样,字符可以一个接一个地传输,但是,在每组信息(通常称为帧)的开始要加上同步字符,在没有信息要传输时,要填上空字符,因为同步传输不允许有间隙。在同步传输过程中,一个字符可以对应5~8位。当然,对同一个传输过程,所 有字符对应同样的数位,比如说n位。这样,传输时,按每n位划分为一个时间片,发送端在一个时间片中发送一个字符,接收端则在一个时间片中接收一个字符。 同步传输时,一个信息帧中包含许多字符,每个信息帧用同步字符作为开始,一般将同步字符和空字符用同一个代码。在整个系统中,由一个统一的时钟控制发送端的发送和空字符用同一个代码。接收端当然是应该能识别同步字符的,当检测到有一串数位和同步字符相匹配时,就认为开始一个信息帧,于是,把此后的数位作为实际传输信息来处理。 异步通信原理 异步通信是一种很常用的通信方式。异步通信在发送字符时,所发送的字符之间的时间间隔可以是任意的。当然,

实现异步串口

异步传输是一种典型的基于字节的输入输出,指数据按每次一个字节进行传输,其传输速度低。同步传输是把数据字节组合起来一起发送,这种组合称之为帧,其传输速度比异步传输快,同步串口的传送速率高,异步串口实现简单,这是异步串口与同步串口间最主要的区别。 一,异步非阻塞串口通讯的优点 读写串行口时,既可以同步执行,也可以重叠(异步)执行。 在同步执行时,函数直到操作完成后才返回。这意味着在同步执行时线程会被阻塞,从而导致效率下降。 在重叠执行时,即使操作还未完成,调用的函数也会立即返回。费时的I/O操作在后台进行,这样线程就可以干别的事情。 例如,线程可以在不同的句柄上同时执行I/O操作,甚至可以在同一句柄上同时进行读写操作。"重叠"一词的含义就在于此。 二,异步非阻塞串口通讯的基本原理 首先,确定要打开的串口名、波特率、奇偶校验方式、数据位、停止位,传递给CreateFile()函数打开特定串口; 其次,为了保护系统对串口的初始设置,调用 GetCommTimeouts()得到串口的原始超时设置; 然后,初始化DCB对象,调用SetCommState() 设置DCB,调用SetCommTimeouts()设置串口超时控制; 再次,调用SetupComm()设置串口接收发送数据的缓冲区大小,串口的设置就基本完成,之后就可以启动读写线程了。 三,异步非阻塞串口通讯的基础知识 VC串口通信技术网下面来介绍并举例说明一下编写异步非阻塞串口通讯的程序 中将会使用到的几个关键函数 CreateFile() 功能:打开串口设备 函数原型 1.HANDLE CreateFile( 2.LPCTSTR lpFileName, // 串口名称字符串;如: "COM1" 或 "COM2" 3.DWORD dwDesiredAccess, // 设置读写属性(访问模式);一般为 GENERIC_READ|GENERIC_WRITE, 4.DWORD dwShareMode, // 共享模式;"必须"为 0, 即不能共享 5.LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性;一般为 NULL 6.DWORD dwCreationDistribution, // 创建方式,串口设置必须设置此值; 在这里"必须"为 OPEN_EXISTING 7.DWORD dwFlagsAndAttributes, // 文件属性和标志;在这里我们设置成 FILE_FLAG_OVERLAPPED ,实现异步I/O 8.HANDLE hTemplateFile // 临时文件的句柄,通常为NULL

设定目标的15个步骤和方法(精)

设定目标的15个步骤和方法 1热切的期待和欲望?问自己:“真的很希望得到吗?”强烈的愿望是人类一切活动的原动力。欲望越强烈,决心越大,而自己也才愿意付出代价。2目标必须是明确的,可达到的,可以衡量的? 只有明确而具体的目标才可衡量,而只有可衡量的目标才可能达到。否则只可能是笼统、空泛的无意义的大话而已。3把目标写下来当你在书写时,你的思维活动在记忆中产生一种不可磨灭的印象,它告诉你的潜意识:这是真的。我不相信我的记忆,我只相信我的笔记。4问自己:“为什么要实现这个目标”写出实现这个目标的理由,好处和意义理由或好处越多越好。这样做,有助于发现、认识目标的必要性和重要性,从而增加实现目标的紧迫感,获得强大的驱动力。5规定实现目标的期限没有期限,就等于没有目标,就永远达不到成功的彼岸。期限,是衡量目标进展的尺度,是激发你向目标不断前进的动力。6分析你的起始点没有理想,就没有前进的方向。没有起始点,就无从规划自己的航程;即使有了地图和指南针,仍然会无可奈何的迷失方向,只有明确自己现在所处的位置,地图和指南针才能发挥作用。分析起始点,就是弄清现在所处的环境和条件。7确认实现目标的障碍,并依“难度”设定优先顺序人不是为了痛苦而活着,是为了幸福才活着,而痛苦却伴随着人生。确认障碍,是为;了有备无患,从容不迫。‘同时要记住:障碍是对我们的锻炼和考验,而不能阻碍我们的前进。每前进一步都会有障碍,实现目标的过程,就是克服障碍的过程。8 确认达到目标所需的知识和技能为了实现目标而不断完善自己,作好知识和技能的充分准备。生命不息,学习不止。9确认对实现目标有帮助的人和团体调动一切可以调动的力量和因素,来帮助自己实现目标。10找出克服障碍的方法对关键性障碍应找出不少于五个解决方案,其他每个障碍都有有克服的办 法。11制定实现目标的计划一但定了目标及实现目标(克服障碍的方法,就要制定每年、每月、每周甚至每天的计划。计划,就是目标分解一览表。12将你的计划付诸行动,立即去做没有行动再好的行动也只是白日梦。不要拖延,不要“以后”,立即就做,现在就做。13将目标视觉化想象目标实现以后的情景,将自己沉浸在成功的快乐中。描绘一幅明晰的胜利景象,激发现实目标的动力和克服障碍的决心。14以坚定的信念支持你的目标任何事,

struts2+json+jquery实现ajax登录和注册功能

在上一篇博文中已经学习了如何整合mybatis和spring,实现持久层的CRUD操作、业务层的事务管理和spring的IoC。 现在我们这个demo的基础上,继续整合struts2,并利用json插件和jquery实现ajax,完成后系统将实现登录与注册的简单功能。 浏览器端如何简单、高效地与服务器端进行数据交互是web开发者最为关心的内容。在客户端构造intput表单数据或者拼凑URL参数名称/参数值,然后在服务器端笨拙地用request.getParameter(“参数名称”)获取参数显然已经过时了,在struts2中,只要在action 里定义了input表单名称/URL参数名称对应的String类型属性,并设置getter和setter 方法,struts2在调用action的时候就可以根据参数值自动帮你设置好action中对应的属性值供你使用,这极大地方便了开发者。 但是json更为强大——它可以根据浏览器端上传的符合格式的数据设置action中对象的值,也就是说,struts2自动封装的数据只有一层,而json是无限层。 json给页面参数的传递带来极大的方便,结合jquery来使用,可以轻易地做到页面局部刷新、页面无跳转进行友好的系统异常提示等,其中后者是我觉得最有必要做到的一点,在action中定义一个message变量,把action方法执行的结果或者系统异常信息放到里面以json的方式返回给客户端,客户端根据这个变量的结果来进行下一步的操作或者提示系统异常信息,非常地好用。 json由javascript中的对象和数组构成,基本形式是{key:value},key为属性名称,value 为属性值,value可以为数字、字符串、数组、对象,value可以为数组和对象是json可以封装无限层数据的关键所在。至于如何建构和解析json不是本篇博文的详细表述范围,请参考其他资料。 现在就让我们利用struts2作为MVC框架,整合json插件,在浏览器端使用jquery解析和系列化json数据,由此制作一个具有登陆/注册功能的小demo。本demo中的数据持久层的实现用到了mybatis3和spring3,请参考本人的上一篇博文。 首先我们需要在eclipse中新建一个web工程,并把以下jar包拷贝到工程WEB-INF/lib 下: aopalliance-1.0.jar asm-3.3.1.jar asm-commons-3.3.jar asm-tree-3.3.jar aspectjweaver.jar cglib-2.2.2.jar commons-dbcp-1.2.1.jar

异步传输和同步传输的区别(整理)

同步传输和异步传输的区别 在网络通信过程中,通信双方要交换数据,需要高度的协同工作。为了正确的解释信号,接收方必须确切地知道信号应当何时接收和处理,因此定时是至关重要的。在计算机网络中,定时的因素称为位同步。同步是要接收方按照发送方发送的每个位的起止时刻和速率来接收数据,否则会产生误差。通常可以采用同步或异步的传输方式对位进行同步处理。 1. 异步传输(Asynchronous Transmission):异步传输将比特分成小组进行传 送,小组可以是8位的1个字符或更长。发送方可以在任何时刻发送这些比特组,而接收方从不知道它们会在什么时候到达。一个常见的例子是计算机键盘与主机的通信。按下一个字母键、数字键或特殊字符键,就发送一个8比特位的ASCII代码。键盘可以在任何时刻发送代码,这取决于用户的输入速度,内部的硬件必须能够在任何时刻接收一个键入的字符。 异步传输存在一个潜在的问题,即接收方并不知道数据会在什么时候到达。在它检测到数据并做出响应之前,第一个比特已经过去了。这就像有人出乎意料地从后面走上来跟你说话,而你没来得及反应过来,漏掉了最前面的几个词。因此,每次异步传输的信息都以一个起始位开头,它通知接收方数据已经到达了,这就给了接收方响应、接收和缓存数据比特的时间;在传输结束时,一个停止位表示该次传输信息的终止。按照惯例,空闲(没有传送数据)的线路实际携带着一个代表二进制1的信号,异步传输的开始位使信号变成0,其他的比特位使信号随传输的数据信息而变化。最后,停止位使信号重新变回1,该信号一直保持到下一个开始位到达。例如在键盘上数字“1”,按照8比特位的扩展ASCII编码,将发送“00110001”,同时需要在8比特位的前面加一个起始位,后面一个停止位。 异步传输的实现比较容易,由于每个信息都加上了“同步”信息,因此计时的漂移不会产生大的积累,但却产生了较多的开销。在上面的例子,每8个比特要多传送两个比特,总的传输负载就增加25%。对于数据传输量很小的低速设备来说问题不大,但对于那些数据传输量很大的高速设备来说,25%的负载增值就相当严重了。因此,异步传输常用于低速设备。 2. 同步传输(Synchronous Transmission):同步传输的比特分组要大得多。它 不是独立地发送每个字符,每个字符都有自己的开始位和停止位,而是把它们组合起来一起发送。我们将这些组合称为数据帧,或简称为帧。 数据帧的第一部分包含一组同步字符,它是一个独特的比特组合,类似于前面提到的起始位,用于通知接收方一个帧已经到达,但它同时还能确保接收方的采样速度和比特的到达速度保持一致,使收发双方进入同步。 帧的最后一部分是一个帧结束标记。与同步字符一样,它也是一个独特的比特串,类似于前面提到的停止位,用于表示在下一帧开始之前没有别的即将到达的数据了。

相关文档
最新文档