系统级编程Lab 14. Exception and Process/Thread
来源:互联网 发布:中国出口构成 知乎 编辑:程序博客网 时间:2024/05/18 17:44
系统级编程最后一周的实验,一共5个Practice,因为网上没有相关的解答,我给出自己的解答,有兴趣的可以参考参考,有误的可以发送我的邮箱doglikejie@163.com交流
这是ppt的题目要求
我分析之后给出我的源代码
Practice1
第一题是在鼠标点击位置显示时间
我使用了以下重要函数来实现这一功能
GetLocalTime(),这个函数的使用,我先定义了一个SYSTEMTIME类型的变量systime,然后GetLocalTime(&systime);这一行代码把本地时间储存在systime变量里。
然后很重要的一个函数wsprintf(),为什么我这里使用wsprintf函数而不是老师给的sprintf函数,因为我在vs2017环境下编译意外的发现sprintf函数无法被识别,具体原因我也不知道,所以就用了wsprintf函数代替。
wsprintf(str, "[%d年%d月%d日%d时%d分%d秒] ",
systime.wYear, systime.wMonth, systime.wDay,
systime.wHour, systime.wMinute, systime.wSecond);
因为windows编程没有printf这种函数,所以我用wsprintf函数的目的是为了将时间保存为字符串便于打印。我们利用systime.wYear等进一步确定精确的年月日时分秒。
x = LOWORD(lParam);
y = HIWORD(lParam);
这两行分别保存横坐标和纵坐标,关于如何保存鼠标的问题,我查阅网上发现不同的解法,我这里偷懒直接int两个变量x,y保存,网上的通解应该是POINT 一个变量,然后通过变量.x,变量.来储存横纵坐标,这两种方法的优劣我还没思考过。
hdc = GetDC(hwnd);
//输出字符串
TextOut(hdc, x, y, str, strlen(str));
ReleaseDC(hwnd, hdc);
GetDC()和ReleaseDC()必须成对出现,GetDC之后必须释放掉。TextOut负责打印字符串,参数的用法可以查阅msdn。
#include <windows.h>
LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance,
PSTR szCmdLine,int iCmdShow)
{
static TCHAR szAppName[] =TEXT ("HelloWin") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW |CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL,IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL,IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL,TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, // window class name
TEXT ("The Hello Program"),// window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg,NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
RECT rect ;
int x, y;//分别储存横纵坐标
CHAR str[100];//储存转换成字符串的时间
SYSTEMTIME systime;//负责储存系统时间
switch (message)
{
case WM_CREATE:
//PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
return 0 ;
case WM_LBUTTONDOWN:
x = LOWORD(lParam);
y = HIWORD(lParam);
GetLocalTime(&systime);
wsprintf(str, "[%d年%d月%d日%d时%d分%d秒] ",
systime.wYear, systime.wMonth, systime.wDay,
systime.wHour, systime.wMinute, systime.wSecond);
hdc = GetDC(hwnd);
//输出字符串
TextOut(hdc, x, y, str, strlen(str));
ReleaseDC(hwnd, hdc);
break;
return 0;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
GetClientRect (hwnd, &rect) ;
DrawText (hdc, TEXT ("Hello, world"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd,message,wParam,lParam) ;
}
上面是我修改后的源代码
截图明显看出已经正确的实现了功能。当然我也思考过如何打印一次时间把上一次的使时间消除掉。因为老师没有要求,所以我也没有花时间实现这个功能了。感兴趣的可以自己尝试尝试。
Practice2 Add thread to DrawRect
Add thread to solve the problem in the program
题目源代码运行后,主要问题是,当我们左击鼠标一切正常,然而我们右击鼠标时,程序会进入一个无限画画的死循环,题目要求的是添加线程以解决问题。现在给出分析和源代码。
这里我用的是CreateThread()函数来建立新线程,下面是CreateThread()函数的定义,我们接下来将根据这个定义建立线程。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性,一般为NULL
DWORD dwStackSize, // 初始的栈大小,给NULL即可(系统会设置默认值)
LPTHREAD_START_ROUTINE lpStartAddress, // 线程函数地址
LPVOID lpParameter, // 线程参数,传递给线程函数的参数
DWORD dwCreationFlags, // 创建的操作,如挂起等操作,为NULL则立即运行
LPDWORD lpThreadId // 线程的ID,一般不需要,给NULL
);
同时我们将while(1)那整个循环放入到线程函数中
DWORD WINAPI printThread(LPVOID lpParameter)
{
HBRUSH hBrush;
HDC hdc;
int xLeft, xRight, yTop, yBottom, iRed, iGreen, iBlue;
while (1)
{
if (cxClient != 0 || cyClient != 0)
{
xLeft = rand() % cxClient;
xRight = rand() % cxClient;
yTop = rand() % cyClient;
yBottom = rand() % cyClient;
iRed = rand() & 255;
iGreen = rand() & 255;
iBlue = rand() & 255;
hdc = GetDC(hwnd);
hBrush = CreateSolidBrush(RGB(iRed, iGreen, iBlue));
SelectObject(hdc, hBrush);
Rectangle(hdc, min(xLeft, xRight), min(yTop, yBottom),
max(xLeft, xRight), max(yTop, yBottom));
ReleaseDC(hwnd, hdc);
DeleteObject(hBrush);
}
}
return 0;
}
接下来每点击右键一次,新建一个线程,然而那个线程并行运行,因此并不影响我们“看到”的线程的操作。
现在是左键情况,我们现在点击右键观察
这里注意右边诊断绘画里进行内存的变化,很明显看出进行内存使用变多,意思也就是那些线程被成功创建了!
#include <windows.h>
#include <process.h>
LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM) ;
HWND hwnd ;
int cxClient, cyClient ;
int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance,
PSTR szCmdLine,int iCmdShow)
{
static TCHAR szAppName[] =TEXT ("RndRctMT") ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW |CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL,IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL,IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL,TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName,TEXT ("Random Rectangles"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg,NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
DWORD WINAPI printThread(LPVOID lpParameter)
{
HBRUSH hBrush;
HDC hdc;
int xLeft, xRight, yTop, yBottom, iRed, iGreen, iBlue;
while (1)
{
if (cxClient != 0 || cyClient != 0)
{
xLeft = rand() % cxClient;
xRight = rand() % cxClient;
yTop = rand() % cyClient;
yBottom = rand() % cyClient;
iRed = rand() & 255;
iGreen = rand() & 255;
iBlue = rand() & 255;
hdc = GetDC(hwnd);
hBrush = CreateSolidBrush(RGB(iRed, iGreen, iBlue));
SelectObject(hdc, hBrush);
Rectangle(hdc, min(xLeft, xRight), min(yTop, yBottom),
max(xLeft, xRight), max(yTop, yBottom));
ReleaseDC(hwnd, hdc);
DeleteObject(hBrush);
}
}
return 0;
}
LRESULT CALLBACK WndProc (HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
return 0;
case WM_RBUTTONDOWN:
HANDLE hThread = CreateThread(0, 0, printThread, (LPVOID)10,CREATE_SUSPENDED, 0);
return 0 ;
case WM_LBUTTONDOWN:
MessageBox(hwnd,"Left button down!","点击左键",MB_OK);
return 0;
case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd,message,wParam,lParam) ;
}
这是源代码
注意在vs下编译 HANDLE hThread = CreateThread(0, 0, printThread, (LPVOID)10, CREATE_SUSPENDED, 0);会报错,但是却不影响使用,所以我也没深究了。
P3
新建两个线程然后让他们在主函数里运行之后用WaitForMultipleObjects函数等待线程结束,P3是SSD6 Exercise6的原题,我在这里也就直接给出简单分析和标准答案了。
/*
ThreadedClient.cpp
A threaded database client.
*/
#include <iostream>
#include "servers.h"
#ifdef _MSC_VER
#include <windows.h>
#include <winbase.h>
#include <process.h>
typedef __int64 INT64_T;
typedef unsigned __int64 UINT64_T;
typedef LONGLONG time_ms_t;
time_ms_t getTimeInMilliseconds() {
SYSTEMTIME stime;
GetSystemTime( &stime );
FILETIME ftime;
LARGE_INTEGER time;
SystemTimeToFileTime( &stime, &ftime ); /* if this fails... */
time.HighPart = ftime.dwHighDateTime;
time.LowPart = ftime.dwLowDateTime;
/* FileTime is in 100ns intervals since 1/1/1601 */
return time.QuadPart / 10000;
}
#endif
/*
ostream &operator <<( ostream &out, string *str ) {
Send a string to an output stream.
*/
ostream &operator<<(ostream &out,string *str) {
if (str)
return out << str->data();
else
return out;
}
/*
ostream & operator<< ( ostream &out, INT64_T num )
Print a 64-bit unsigned integer to the given output stream.
*/
ostream &operator<<(ostream &out,INT64_T snum ) {
#define _OSTR_LL_LEN 21
if (snum) {
char buffer[_OSTR_LL_LEN];
int i = _OSTR_LL_LEN - 1;
UINT64_T num;
if (snum < 0) num = -snum;else num =snum;
buffer[i] = '\0';
while (num) {
buffer[--i] = ('0' + (int) (num % 10));
num /= 10;
}
if (snum < 0) buffer[--i] ='-';
return out << buffer + i;
}else return out << '0';
}
/*
These are the two thread routines, each of which will run as
as a separate thread
*/
Personal *global_personal = NULL;
void __cdecl personal_thread(void *arg ) {
int acct = *( (int *)arg );
global_personal = GetPersonalInformation( acct );
}
AccountInfo *global_account = NULL;
void __cdecl account_thread(void *arg ) {
int acct = *( (int *)arg );
global_account = GetAccountInformation( acct );
}
/*
int main( int argc, char *argv[]
You should modify this function to use threads.
*/
int main( int argc, char *argv[] ) {
HANDLE handles[2];
if (argc != 2) {
cerr << "usage: " << argv[0]<< " [account_number]" << endl;
exit(1);
}
int account = atoi( argv[1] );
time_ms_t start = getTimeInMilliseconds();
cout << "Retrieving...";
cout.flush();
handles[0] = (HANDLE) _beginthread( personal_thread, 0, &account );
if (handles[0] == (HANDLE) -1L) {
cout << "Couldn't create thread!" << endl;
exit(1);
}
handles[1] = (HANDLE) _beginthread( account_thread, 0, &account );
if (handles[1] == (HANDLE) -1L) {
cout << "Couldn't create thread!" << endl;
exit(1);
}
WaitForMultipleObjects( 2, handles, TRUE,INFINITE );
cout << "done" << endl;
time_ms_t end = getTimeInMilliseconds();
cout << "done (" << end - start <<"ms)" << endl;
if (global_personal) {
cout << account << ": " << global_personal->FirstName<< " "
<< global_personal->LastName << endl;
cout << global_personal->Address<< endl;
cout << "Balance: " << global_account->Balance<< ", "
<< global_account->Pending
<< " pending, " << global_account->Share<< " share" << endl;
}
delete global_personal;
delete global_account;
return 0;
}
P4
程序问题
打印出的count的值随机,
现象名称:(终止)异常
发生原因:_sleep()函数提前终止了线程运行,将_sleep()函数删去,发现运行结果正常为0,
#include <stdio.h>
#include <process.h>
#include <windows.h>
int count=0;
void thread1(void*pvoid)
{
while(count<1000000000)
{
count++;
}
}
void thread2(void*pvoid)
{
while(count>-1000000000)
{
count--;
}
}
int main()
{
_beginthread(thread1,0,NULL);
_beginthread(thread2,0,NULL);
//_sleep(100);
printf("%d\n",count);
getchar();
return 0;
}
P5单选题,SSD6有现成的答案,我写这篇博客的原因是因为之前的lab'中参阅了不少前辈们的答案,在做lab14发现暂无答案,希望自己的思路能帮助到后面的同学。