Cooperative Multithreading in BREW with IThread

本文介绍如何在BREW应用环境中利用合作式多线程避免长时间阻塞CPU,通过具体示例展示了IThread接口的基本使用方法。
 
Cooperative Multithreading in BREW with IThread
Learn how to harness cooperative multithreading in BREW to avoid writing applications that block the CPU during lengthy operations
by Ray Rischpater
August 2, 2006  
http://www.devx.com/wireless/Article/32077?trk=DXRSS_WIFI
 
My last article mentioned the BREW IThread as a possible solution to the challenges of dealing with blocking code on BREW. Whether you're porting existing code to BREW or writing new code, the IThread interface can be quite handy. Sadly, in my experience it's one of the most misunderstood and maligned interfaces provided by BREW—developers seem to overlook it and write code which provides the same functionality (using AEECallback and ISHELL_Resume), often because they have decided that since BREW doesn't provide preemptive multithreading there's little point in using IThread. Unfortunately, this approach leads to additional testing and debugging, as well as code that requires additional documentation (or time invested in understanding where no documentation is presented).
 
This article begins by explaining the basic principle behind cooperative multithreading and then shows you the basics of the IThread interface. Rather than just give you some code samples along the way, you can download hellothread.c and follow along; bits of the actual file are included here for your reference.
 
Understanding Cooperative Multithreading
Most readers are comfortable with the basic notion of a thread of execution: you create a thread using some system API such as pthread_create, passing a function pointer called the thread's main function. This function executes in a separate thread of execution in the same memory space at the same time as the main thread of execution, and you can share data using synchronization tools such as mutexes. When your main function exits or you call the equivalent of pthread_exit, your thread terminates and any functions joined to the thread using pthread_join are invoked at that time. Under the hood, the native operating system (typically in conjunction with a user-level library) is responsible for doing the work of sharing processor and memory and providing mutexes and the like. Because the host platform and OS support threads natively, there are typically some guarantees regarding the availability of resources—one thread can't starve another thread for CPU access, for example. For this reason, these environments are called preemptive, because the operating system can preempt one thread (or process) another to ensure that everything gets a fair share of resources.
Not so with the BREW application environment at present, where memory and processor are shared cooperatively. That is, if your application is running, it owns the CPU for the duration of an event handler's execution; spend too long handling an event or callback, and the handset will reset, because its watchdog timer has assumed that your application has crashed. Many developers criticize this approach as placing an unwelcome burden on application developers, and in truth it does make some kinds of applications (those requiring a guarantee of deterministic execution) impossible to create. However, the majority of applications do not have critical scheduling requirements, and, in fact, cooperative threads are not inordinately more complex than their preemptive kin. There are two key differences, however. First, threads on BREW must yield to the processor occasionally, giving other threads of execution, which requires two function calls (or one, if you'd like to write a wrapper function). Second, because your threads explicitly yield the processor, you don't need synchronization interfaces when accessing shared data, because only one thread is running during the shared data access.
Starting a New IThread
This article's application is a simple "hello world" application, thread-style (see Figure 1). It increments two counters in two threads, and just for fun, one thread also draws circles of incrementally larger size on the display. To show how threads can return values to the owning program, one thread exits after a set number of iterations; the other does not.
An IThread is an interface, just like any other, so you can make one simply by calling ISHELL_CreateInstance. Before you do, however, you should have someplace with appropriate scope to contain the reference to the thread, as well as a structure to contain any thread-local storage that the thread would want to share between the functions it invokes:
typedef struct _SThreadCtx
{
 IThread *pit;
 int n;
 IDisplay *pid;
 IShell *pis;
 IGraphics *pig;
} SThreadCtx;
 
 
typedef struct _CApp
{
 AEEApplet a;
 SThreadCtx stc1, stc2;
 AEECallback cbThread2Done;
 int nThread2Result;
} CApp;
 
 
This is pretty simple stuff here. The application's threads will use an integer, access to the display, shell, and an IGraphics interface; all of this is wrapped in an SThreadCtx. The application itself will have two threads; for now, don't concern yourself with the purpose of the cbThread2Done or nThread2Result members of the CApp structure.
With the heap use out of the way, you can create and initialize your threads when your application begins. In the application's event handler, write:
 
    case EVT_APP_START:
    {
 
      int r;
      r = ISHELL_CreateInstance( p->m_pIShell, AEECLSID_THREAD, 
                                 (void **)&pMe->stc1.pit );
      r = ISHELL_CreateInstance( p->m_pIShell, AEECLSID_THREAD, 
                                 (void **)&pMe->stc2.pit );
      r = ISHELL_CreateInstance( p->m_pIShell, AEECLSID_GRAPHICS, 
                                 (void **)&pMe->stc2.pig );
      pMe->stc1.pis = pMe->stc2.pis = p->m_pIShell;
      pMe->stc1.pid = pMe->stc2.pid = p->m_pIDisplay;
 
      CALLBACK_Init( &pMe->cbThread2Done, (PFNNOTIFY)Thread2Done, (void *)pMe );
      ITHREAD_HoldRsc( pMe->stc2.pit, (IBase *)pMe->stc2.pig );
      ITHREAD_Join( pMe->stc2.pit, &pMe->cbThread2Done, &pMe->nThread2Result );
      ITHREAD_Start( pMe->stc1.pit, 128, (PFNTHREAD)Thread1Start, 
                    (void *)&pMe->stc1);
      ITHREAD_Start( pMe->stc2.pit, 128, (PFNTHREAD)Thread2Start, 
                     (void *)&pMe->stc2);
      IDISPLAY_DrawText( p->m_pIDisplay, AEE_FONT_BOLD, szText, -1, 0, 0, NULL,
                         IDF_ALIGN_CENTER | IDF_ALIGN_MIDDLE);
      IDISPLAY_Update (p->m_pIDisplay);
    }
 
    return(TRUE);
This code:
  • Creates two IThread instances.
  • Creates the IGraphics instance required by the circle-drawing thread.
  • Joins the function Thread2Done with the second thread, so that it will be invoked when Thread2 exits.
  • Indicates that thread 2 will use the IGraphics interface.
  • Calls each thread's start function.
  • Draws "Hello World" to the display.
  • Updates the display.
The IThread calls are quite similar to POSIX thread calls. Qualcomm has also added the ITHREAD_HoldRsc and ITHREAD_ReleaseRsc interfaces to associate a BREW interface with a thread. The BREW ITHREAD_HoldRsc interface attaches a BREW interface to a thread. Oddly, it does not update the item's reference count, so don't make the mistake of releasing the held interface when you call ITHREAD_HoldRsc.
For clarity, I've broken out the thread's main function and start function—you don't have to do this, but to me main on BREW suggests an event loop, whose first argument is the application context. By comparison, the PFNTHREAD function prototype for an IThread's main function has two arguments, the running thread and the thread context. Thus, my Thread1Start and Thread2Start functions look like this:
 
static void Thread1Start( IThread *pThread, SThreadCtx *pMe )
{
 Thread1Main( pMe );
}
 
 
static void Thread2Start( IThread *pThread, SThreadCtx *pMe )
{
 Thread2Main( pMe );
}
This imposes a bit of space overhead in return for improved readability; in addition it provides a nice abstraction point in case I need to do additional setup work in the new thread before giving it something to do.
Yielding the Processor
Yielding the processor in an IThread is done using ISHELL_Resume and ITHREAD_GetResumeCBK:
 
static void Thread1Main( SThreadCtx *pMe )
{
 AECHAR wsz[ 16 ];
 
 while( 1 )
 {
    pMe->n++;
    WSPRINTF( wsz, sizeof( wsz ), L"t1=%d", pMe->n );
    IDISPLAY_DrawText( pMe->pid, AEE_FONT_NORMAL, wsz, -1, 0, 0, NULL, 
                       IDF_ALIGN_CENTER | IDF_ALIGN_TOP);
    IDISPLAY_Update (pMe->pid);
 
    ISHELL_Resume( pMe->pis, ITHREAD_GetResumeCBK( pMe->pit ) );
    ITHREAD_Suspend( pMe->pit );
 }
}
What raises eyebrows about this code for experienced BREW developers is the fifth line—an infinite loop, a definite no-no. Otherwise, it's quite straightforward, until the last two lines, which obtains the AEECallback instance for this thread at this particular point in execution, and then suspends thread execution. Under the hood, this code sets up the system's notion of the thread context at this point in your function, so when ISHELL_Resume resumes execution, it will do so at the current point in your function where you called ITHREAD_Suspend. This is functionally equivalent to a call to the Qualcomm-provided IThread_Yield, available in the BREW SDK's src/ directory in the file AEETU_Yield.c:
 
void IThread_Yield( IThread *me, IShell *pIShell )
{
 AEECallback *pcb = ITHREAD_GetResumeCBK( me );
 ISHELL_Resume( pIShell, pcb );
 ITHREAD_Suspend( me );
}
Why did Qualcomm break out the thread callback from the resume operation? Not just to keep us typing two lines instead of one, but to provide access to the callback for any callback-consuming function. Consider the Qualcomm-provided IThread_GetHostByName, which mimicks a blocking gethostbyname call, familiar to desktop developers using the Berkeley sockets interface:
 
int IThread_GetHostByName( IThread *me, INetMgr *piNet, 
                           AEEDNSResult *pRes, const char *pszHost)
{
 
 AEECallback *pcb = ITHREAD_GetResumeCBK(me);
 
 INETMGR_GetHostByName(piNet, pRes, pszHost, pcb);
 ITHREAD_Suspend(me);
 
 if (((AEEDNSResult *)0 != pRes) && 
       (pRes->nResult > 0) && (pRes->nResult <= AEEDNSMAXADDRS))
 {
    return pRes->nResult;
 }
 else
 {
    return 0;
 }
}
By wrapping the INETMGR_GetHostByName interface within its own thread, you can simulate blocking behavior by using the thread's own AEECallback as the callback the InetMgr invokes upon completion. Once the INetMgr invokes the callback, execution continues on the line after the ITHREAD_Suspend invocation as if the pair of calls to INetMgr and IThread were a synchronous call. This trick should work anywhere an interface consumes an AEECallback; see the thread utilities provided by Qualcomm in recent drops of the SDK.
Obtaining the Result of an IThread's Execution
When a thread exits, it can provide a return value to the thread originator. Because of the prohibition on global variables in early versions of BREW, you must also provide the integer variable to store the return value. Remember the call to ITHREAD_Join in my EVT_APP_START handler?
 
CALLBACK_Init( &pMe->cbThread2Done, (PFNNOTIFY)Thread2Done, (void *)pMe );
ITHREAD_Join( pMe->stc2.pit, &pMe->cbThread2Done, &pMe->nThread2Result );
This causes the IThread to execute the callback function indicated by pMe->cbThread2Done when the thread terminates. Within this function, you can get the exit value (an integer) like this:
 
static void Thread2Done( CApp *pMe )
{
 DBGPRINTF( pMe->nThread2Result == SUCCESS ? 
               "Thread2 exit SUCCESS" : 
               "Thread2 exit FAILED" );
}
You can use ITHREAD_Join anytime you need a programmatic action on thread termination.
Give IThread a Second Chance!
For those new to BREW, it's important to realize that BREW has threads—they're just cooperative. And if you're an old saw with BREW, consider using BREW's IThread to replace some of your callback chains. You won't be disappointed.
 
 ===================================================================
hellothread.c
===================================================================
/*
 * File: HelloWorld.c
 * Author: Ray Rischpater (Rocket Mobile, Inc.)
 * Purpose:
 *   Demonstrate the use of IThread
 *
 */

#include "AEE.h"
#include "AEEStdLib.h"
#include "AEEAppGen.h"        // Applet helper file
#include "helloworld.bid"    // Applet-specific header that contains class ID
#include "nmdef.h"
#include "AEEThread.h"
#include "AEEGraphics.h"
typedef struct _SThreadCtx
{
  IThread  *pit;
  int n;
  IDisplay *pid;
  IShell *pis;
  IGraphics *pig;
} SThreadCtx;
typedef struct _CApp
{
  AEEApplet a;
  SThreadCtx stc1, stc2;
  AEECallback cbThread2Done;
  int nThread2Result;
} CApp;
/*-------------------------------------------------------------------
Static function prototypes
-------------------------------------------------------------------*/
static void Thread1Start( IThread *pThread, SThreadCtx *pMe );
static void Thread2Start( IThread *pThread, SThreadCtx *pMe );
static void Thread1Main( SThreadCtx *pMe );
static void Thread2Main( SThreadCtx *pMe );
static void Thread2Done( CApp *pMe );
static boolean HelloWorld_HandleEvent(AEEApplet * pme, AEEEvent eCode,uint16 wParam, uint32 dwParam);
int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * pMod,void ** ppObj);
/*===============================================================================
FUNCTION DEFINITIONS
=============================================================================== */
int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * pMod,void ** ppObj)
{
  *ppObj = NULL;
  if(AEEApplet_New( sizeof(CApp),                  // Size of our private class
    ClsId,                              // Our class ID
    pIShell,                            // Shell interface
    pMod,                               // Module instance
    (IApplet**)ppObj,                   // Return object
    (AEEHANDLER)HelloWorld_HandleEvent, // Our event handler
    NULL))                              // No special "cleanup" function
    return(AEE_SUCCESS);
  return (EFAILED);
}
static void Thread1Start( IThread *pThread, SThreadCtx *pMe )
{
  Thread1Main( pMe );
}
static void Thread1Main( SThreadCtx *pMe )
{
  AECHAR wsz[ 16 ];
  while( 1 )
  {
    pMe->n++;
    WSPRINTF( wsz, sizeof( wsz ), L"t1=%d", pMe->n );
    IDISPLAY_DrawText(pMe->pid,    // Display instance
      AEE_FONT_NORMAL,     // Use BOLD font
      wsz,         // Text - Normally comes from resource
      -1,                  // -1 = Use full string length
      0,                   // Ignored - IDF_ALIGN_CENTER
      0,                   // Ignored - IDF_ALIGN_MIDDLE
      NULL,                // No clipping
      IDF_ALIGN_CENTER | IDF_ALIGN_TOP);
    IDISPLAY_Update (pMe->pid);
    ISHELL_Resume( pMe->pis, ITHREAD_GetResumeCBK( pMe->pit ) );
    ITHREAD_Suspend( pMe->pit );
  }
}
static void Thread2Start( IThread *pit, SThreadCtx *pMe )
{
  Thread2Main( pMe );
}
static void Thread2Main( SThreadCtx *pMe )
{
  AECHAR wsz[ 16 ];
  while( pMe->n < 256 )
  {
    AEECircle circle;
      pMe->n++;
      circle.cx = 176/2;
      circle.cy = 160;
      circle.r = pMe->n % 10;
      WSPRINTF( wsz, sizeof( wsz ), L"t2=%d", pMe->n );
      IDISPLAY_DrawText(pMe->pid,    // Display instance
        AEE_FONT_NORMAL,     // Use BOLD font
        wsz,         // Text - Normally comes from resource
        -1,                  // -1 = Use full string length
        0,                   // Ignored - IDF_ALIGN_CENTER
        0,                   // Ignored - IDF_ALIGN_MIDDLE
        NULL,                // No clipping
        IDF_ALIGN_CENTER | IDF_ALIGN_BOTTOM);
      IDISPLAY_EraseRgn( pMe->pid, 0, 150, 176, 20 );
      IGRAPHICS_DrawCircle( pMe->pig, &circle );
      IDISPLAY_Update (pMe->pid);
    ISHELL_Resume( pMe->pis, ITHREAD_GetResumeCBK( pMe->pit ) );
    ITHREAD_Suspend( pMe->pit );
  }
  ITHREAD_Exit( pMe->pit, SUCCESS );
}
static void Thread2Done( CApp *pMe )
{
  DBGPRINTF( pMe->nThread2Result == SUCCESS ? "Thread2 exit SUCCESS" : "Thread2 exit FAILED" );
}
static boolean HelloWorld_HandleEvent(AEEApplet * p, AEEEvent eCode, uint16 wParam, uint32 dwParam)

  AECHAR szText[] = {'H','e','l','l','o',' ','W','o', 'r', 'l', 'd', '/0'};
  CApp *pMe = (CApp *)p;
  switch (eCode)
    {
  case EVT_APP_START:                       
    {
      int r;
      r = ISHELL_CreateInstance( p->m_pIShell, AEECLSID_THREAD, (void **)&pMe->stc1.pit );
      r = ISHELL_CreateInstance( p->m_pIShell, AEECLSID_THREAD, (void **)&pMe->stc2.pit );
      r = ISHELL_CreateInstance( p->m_pIShell, AEECLSID_GRAPHICS, (void **)&pMe->stc2.pig );
      pMe->stc1.pis = p->m_pIShell;
      pMe->stc1.pid = p->m_pIDisplay;
        pMe->stc2.pis = p->m_pIShell;
      pMe->stc2.pid = p->m_pIDisplay;
      CALLBACK_Init( &pMe->cbThread2Done, (PFNNOTIFY)Thread2Done, (void *)pMe );
      ITHREAD_HoldRsc( pMe->stc2.pit, (IBase *)pMe->stc2.pig );
      ITHREAD_Join( pMe->stc2.pit, &pMe->cbThread2Done, &pMe->nThread2Result );
      ITHREAD_Start( pMe->stc1.pit, 128, (PFNTHREAD)Thread1Start, (void *)&pMe->stc1 );
      ITHREAD_Start( pMe->stc2.pit, 128, (PFNTHREAD)Thread2Start, (void *)&pMe->stc2 );
     
      IDISPLAY_DrawText(p->m_pIDisplay,    // Display instance
        AEE_FONT_BOLD,       // Use BOLD font
        szText,              // Text - Normally comes from resource
        -1,                  // -1 = Use full string length
        0,                   // Ignored - IDF_ALIGN_CENTER
        0,                   // Ignored - IDF_ALIGN_MIDDLE
        NULL,                // No clipping
        IDF_ALIGN_CENTER | IDF_ALIGN_MIDDLE);
      IDISPLAY_Update (p->m_pIDisplay);
    }
    return(TRUE);
    case EVT_APP_STOP:
      {
        IGRAPHICS_Release( pMe->stc2.pig );
        ITHREAD_Release( pMe->stc1.pit );
        ITHREAD_Release( pMe->stc2.pit );
      }
      return(TRUE);

    default:
      break;
  }
  return(FALSE);
}

 
 
 
 
 
源码直接下载地址: https://pan.quark.cn/s/a4b39357ea24 过采样与欠采样构成了数字信号处理领域中两种基础的采样策略,它们在工程实践应用时各自展现出独特的长处与短处及适用情境。以下将深入阐释这两种采样方法的运作机制,并对它们在实际操作中的区别进行细致对比。 我们首先阐释过采样的核心概念。过采样(Oversampling)一般是指运用高于必要标准频率对模拟信号实施采样。举例而言,当信号频率为70MHz且信号带宽为20MHz时,依据奈奎斯特采样准则,理论上采样频率只需略高于40MHz(即信号带宽频率的两倍)即可达成无失真采样。然而,在现实操作中,系统构造者常常会采用超过140MSPS(每秒百万次采样)的采样速率,这通常超出理论所需。过采样的主要不利之处涵盖:提升ADC输出数据速率,引发FPGA的时序挑战;增大功耗、ADC及FPGA的制造成本。尽管存在这些不足,过采样依然具备其有利之处,例如可提供处理增益、频率规划的伸缩性以及能够处理更宽的信号带宽。 接下来,我们探讨欠采样的基本原理。欠采样(Undersampling)是指以低于理论标准频率对信号进行采样,这在处理高输入信号频率时尤为有效。例如,针对70MHz的中频(IF)信号,通过欠采样能够采用低于40MHz的采样频率进行采样,从而将数据速率降至FPGA,减少时序挑战,节省能量消耗和成本。实现欠采样的关键设计考量在于它能够在系统设计中达成所需的ADC动态性能。 欠采样的优势体现为能够简化硬件构造,比如降低对高速数据捕获的需求,并且在设计条件允许时,可选用较慢的ADC来削减成本。然而,欠采样技术也存在其局限性,例如在ADC的非理想表现可能导致非线性失真,诸如二阶(HD2)和三阶(HD3)谐...
源码链接: https://pan.quark.cn/s/3523d8c4b5d2 ### Qt5.9.1开发的应用程序转换为可安装`.exe`文件的详细流程 #### 一、概述 本资料将系统性地阐述如何将基于Qt5.9.1版本或其他Qt框架版本开发的应用程序转化为可直接安装的`.exe`安装文件。这一过程不仅适用于Qt5.9.1版本,对其他版本的Qt框架开发的应用同样适用。 #### 二、前期准备 在开展相关操作前,需确保已达成以下准备要求: 1. **开发环境配置**: 利用Qt5.9.1或其他版本完成应用程序的开发工作,并保证能够顺利编译出可执行程序。 2. **NSIS安装**: NSIS(Nullsoft Scriptable Install System)作为一个开源的Windows安装系统,能够支持创建专业的安装程序。用户可从官方渠道或可靠来源获取最新版的NSIS并进行安装。 #### 三、制作可执行程序的流程 ##### 3.1 打包应用程序文件 需要将已开发好的Qt应用程序的所有组件和资源整合到一个文件夹中,例如命名为`Qt_Video`。确保该文件夹内包含所有必要的库文件和资源文件,以便应用程序能够独立运行。 ##### 3.2 压缩文件随后,将整个`Qt_Video`文件夹压缩成`.zip`格式的文件。这一步骤可通过Windows内置的压缩工具或第三方软件完成。 ##### 3.3 创建安装文件接下来,借助NSIS将压缩文件转化为安装文件。具体操作如下: 1. **启动NSIS**: 运行NSIS软件并进入其主界面。 2. **选择基于ZIP的安装模式**: 在主界面中选取“**Installer based on ZIP file**...
内容概要:本文介绍了一种结合单像素检测与数据融合技术的千亿体素级多维荧光成像方法,并提供了完整的Matlab代码实现。该方法融合压缩感知理论与单像素成像原理,通过优化测量矩阵设计、重构算法及多维度数据融合策略,实现了在大幅降低数据采集量的前提下,完成高分辨率、高通量的三维荧光成像,特别适用于大规模生物样本的快速、高效成像需求。文中系统阐述了成像系统的建模过程、关键算法的设计思路以及重建性能的优化路径,充分展现了其在超高体素规模下的成像能力与精确重构优势。; 适合人群:面向具备信号处理、光学成像或生物医学工程等相关专业背景的研究生、科研人员及工程技术开发者,尤其适合熟悉Matlab编程并致力于先进成像技术研究与算法复现的专业人士。; 使用场景及目标:①应用于大规模生物组织的三维荧光成像,显著提升成像效率与图像质量;②为单像素成像、压缩感知与多源数据融合等前沿技术提供可复现、可扩展的算法框架;③支撑高维医学影像重建、新型显微成像系统开发及相关科研与工程实践。; 阅读建议:建议结合所提供的Matlab代码进行模块化分析,重点理解测量过程的数学建模与图像重构算法的实现细节,宜在掌握基本理论的基础上开展仿真实验与参数调优,以深入把握核心技术原理与工程实现要点。
下载代码方式:https://pan.quark.cn/s/a4b39357ea24 Node.js 是一种开放源代码且能够在多种操作系统上运行的 JavaScript 执行环境,它使得开发人员能够在服务器端执行 JavaScript 代码。Node.js 采用了 V8 引擎,该引擎是由 Google 为 Chrome 浏览器开发的一个高性能的 JavaScript 解释器。Node.js 的 16.x 版本在其发展历程中占据着重要位置,其中包含了众多新功能以及性能上的改进。标题 "Nodejs16-x64 windows安装包" 指向的是专为 Windows 操作系统设计的 64 位版本的 Node.js 16 安装程序。在 Windows 平台上安装 Node.js 的 64 位版本对于处理大量数据或运行需要高性能的应用程序来说尤为关键,因为 64 位系统能够更有效地利用硬件资源。描述 "Nodejs-16 x64位windows 安装包" 明确了该安装程序是为 Windows 用户准备的,特别是对于那些需要运行 64 位应用程序的用户。x64 表明该版本兼容 64 位架构,意味着它能够充分利用 64 位计算机的内存和处理能力。标签 "Node Nodejs nodejs16" 提供了关于此安装包的核心信息,表明它与 Node.js 相关,并且具体指的是 v16 版本。这些标签有助于进行搜索和分类,从而方便用户找到他们所需要的特定版本。压缩包文件 "node-v16.18.0-x64.msi" 代表实际的安装文件,其中 "v16.18.0" 指示了 Node.js 的具体版本号,"x64" 再次强调了其适用于 64 位系统,而 ".msi" 后缀表明这是一...
源码链接: https://pan.quark.cn/s/3af847fbbec7 在计算机科学与编程领域中,十六进制(Hexadecimal)以及二进制(Binary)是两种关键性的数值表示方法。十六进制属于一种基于16的计数系统,它运用0至9的数字以及字母A至F(分别象征10至15的数值)来呈现数值,与此同时,二进制则是一种基于2的计数系统,仅采用0和1两个符号。掌握这两种进制之间的相互转换对于深入理解计算机内部运作机制具有决定性意义,因为计算机在底层数据的存储与处理环节通常都是以二进制的形式来进行的。将十六进制转换成二进制的过程可以通过以下几个环节得以完成: 1. **单个十六进制符号的转换**:每一个十六进制符号对应着4位二进制序列。具体而言: - 十六进制中的`0`在二进制表达为`0000` - 十六进制中的`1`在二进制表达为`0001` - 十六进制中的`2`在二进制表达为`0010` - 依此类推 - 十六进制中的`9`在二进制表达为`1001` - 十六进制中的`A`或`a`在二进制表达为`1010` - 十六进制中的`B`或`b`在二进制表达为`1011` - 十六进制中的`C`或`c`在二进制表达为`1100` - 十六进制中的`D`或`d`在二进制表达为`1101` - 十六进制中的`E`或`e`在二进制表达为`1110` - 十六进制中的`F`或`f`在二进制表达为`1111` 2. **多位十六进制符号的转换**:针对一个由多个十六进制符号组成的数值,我们可以逐个符号进行转换,并将得到的二进制序列依次拼接。例如,十六进制数`3F`转换成二进制形式为`00111111`。 3. **编程实现方法**:在编程实践过程中,众多编程语言提...
下载代码方式:https://pan.quark.cn/s/a4b39357ea24 **Vue.js 框架全面解析** Vue.js 是一种轻量级且高性能的前端JavaScript框架,因其便捷性、适应性和可扩展性而备受开发者青睐。在“nodejs+vue”的在线购物平台中,Vue.js 主要承担构建用户界面的任务,并提供数据绑定、组件化、路由管理等关键功能。 1. **数据绑定**:Vue.js 的核心优势之一是双向数据绑定,它借助 `v-model` 指令将视图与数据模型建立联系,确保视图层的变动能即时同步到数据模型,同时数据模型的变化也能实时反映在视图上。在在线购物平台中,这一特性可用于商品列表的动态展示和购物车状态的即时调整。 2. **组件化**:Vue.js 提供了功能强大的组件体系,允许开发者将用户界面拆分为独立且可复用的模块。例如,在在线购物平台中,商品展示模块、购物车功能、支付流程等均可封装为组件,从而提升代码的复用性和可维护性。 3. **指令与过滤器**:Vue.js 中的指令如 `v-if`、`v-for` 和 `v-bind` 用于控制元素的渲染方式及行为,过滤器则能对数据进行格式化处理,例如货币显示、时间格式转换等。在在线购物平台中,这些功能有助于更有效地展示商品信息并优化用户交互体验。 4. **计算属性与侦听器**:计算属性能够监测多个数据源并输出计算结果,而侦听器则能在数据变动时执行指定操作。在在线购物平台中,计算属性可用于自动计算购物车总金额,侦听器则可响应库存变动并实时更新商品状态。 5. **Vue Router 路由管理**:在单页应用(SPA)环境中,Vue Router 是不可或缺的组件,它负责管理页面间的导航和...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值