类可以提供一个公有的静态工厂方法,它用来返回这个类的实例,从而可以构造这个类的对象。
静态工厂方法的好处:
-
它与构造函数不同,静态工厂方法具有名字。这个好处可以帮助我们摆脱一个类只能有一个原型相同的构造函数的限制。举个例子来说:
public class A
{
pubilc int sum=0;
public int div=0;
public A(int a,int b)
{
sum=a+b;
//
利用
a
和
b
在这里初始化
A
的实例
}
public A(int a,int b) //
明显的错误
{
div=a/b;
}
}
就像上面代码所示,这么做是绝对不可能的,构造函数的签名只在参数上进行区分,如果我们想用同一种参数实现不同的构造函数,那么构造函数的局限性就肯定了它是做不到的。但是我们可以利用静态工厂方法来轻松解决这个问题:
public class A
{
public int sum=0;
public int div=0;
public static A Sum(int a,int b)
{
A a1=new A();
a1.sum=a+b;
return a1;
}
public static A Div(int a,int b)
{
A a1=new A();
a1.div=a/b;
return a1;
}
}
很明显,我们可以用相同的参数来构造不同的对象了,如:
A a1=A.Sum(5,5);
A a2=A.Div(5,5);
我们做了什么一幕了然。当然这个例子可能有些不伦不类,但是在这里仅仅想说明这个问题,很极端但是很明确。
-
静态工厂方法每次被调用的时候,不要求非得创建一个新的对象。有的时候我们仅仅需要这个类所表示某些项,但是不需要这个类的实例,那么静态工厂方法可以很好的满足这个要求。
-
静态工厂方法可以返回一个原返回类型的子类型的对象。
静态工厂方法的缺点:
-
类如果不含公有的或者受保护的构造函数,就不能被子类化。对于公有的静态工厂所返回的非公有类,也是同样的。如果一个类没有共有的或者受保护的构造函数,那么这个类就不能被继承。
-
静态工厂方法与其他的静态方法没有任何区别。
在
API
文档中,它们不会像构造函数那样被明确标识出来。
posted @
2006-10-18 23:11 Jerry Cat 阅读(1464) |
评论 (2) |
编辑 收藏
Sony笔试题
1.完成下列程序
*
*.*.
*..*..*..
*...*...*...*...
*....*....*....*....*....
*.....*.....*.....*.....*.....*.....
*......*......*......*......*......*......*......
*.......*.......*.......*.......*.......*.......*.......*.......
#include <stdio.h>
#define N 8
int main()
{
int i;
int j;
int k;
---------------------------------------------------------
| |
| |
| |
---------------------------------------------------------
return 0;
}
2.完成程序,实现对数组的降序排序
#include <stdio.h>
void sort( );
int main()
{
int array[]={45,56,76,234,1,34,23,2,3}; //数字任//意给出
sort( );
return 0;
}
void sort( )
{
____________________________________
| |
| |
|-----------------------------------------------------|
}
3.费波那其数列,1,1,2,3,5……编写程序求第十项。可以用递归,也可以用
其
他方法,但要说明你选择的理由。
#include <stdio.h>
int Pheponatch(int);
int main()
{
printf("The 10th is %d",Pheponatch(10));
return 0;
}
int Pheponatch(int N)
{
--------------------------------
| |
| |
--------------------------------
}
4.下列程序运行时会崩溃,请找出错误并改正,并且说明原因。
#include <stdio.h>
#include <malloc.h>
typedef struct{
TNode* left;
TNode* right;
int value;
} TNode;
TNode* root=NULL;
void append(int N);
int main()
{
append(63);
append(45);
append(32);
append(77);
append(96);
append(21);
append(17); // Again, 数字任意给出
}
void append(int N)
{
TNode* NewNode=(TNode *)malloc(sizeof(TNode));
NewNode->value=N;
if(root==NULL)
{
root=NewNode;
return;
}
else
{
TNode* temp;
temp=root;
while((N>=temp.value && temp.left!=NULL) || (N<temp. value && temp.
right
!=NULL
))
{
while(N>=temp.value && temp.left!=NULL)
temp=temp.left;
while(N<temp.value && temp.right!=NULL)
temp=temp.right;
}
if(N>=temp.value)
temp.left=NewNode;
else
temp.right=NewNode;
return;
}
}
华为笔试题
1.请你分别画出OSI的七层网络结构图和TCP/IP的五层结构图。
2.请你详细地解释一下IP协议的定义,在哪个层上面?主要有什么作用?TCP与UDP呢 ?
3.请问交换机和路由器各自的实现原理是什么?分别在哪个层次上面实现的?
4.请问C++的类和C里面的struct有什么区别?
5.请讲一讲析构函数和虚函数的用法和作用。
6.全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的 ?
7.8086是多少位的系统?在数据总线上是怎么实现的?
联想笔试题
1.设计函数 int atoi(char *s)。
2.int i=(j=4,k=8,l=16,m=32); printf(“%d”, i); 输出是多少?
3.解释局部变量、全局变量和静态变量的含义。
4.解释堆和栈的区别。
5.论述含参数的宏与函数的优缺点。
普天C++笔试题
1.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数。
2.写一个函数,将其中的\t都转换成4个空格。
3.Windows程序的入口是哪里?写出Windows消息机制的流程。
4.如何定义和实现一个类的成员函数为回调函数?
5.C++里面是不是所有的动作都是main()引起的?如果不是,请举例。
6.C++里面如何声明const void f(void)函数为C程序中的库函数?
7.下列哪两个是等同的
int b;
A const int* a = &b;
B const* int a = &b;
C const int* const a = &b;
D int const* const a = &b;
8.内联函数在编译时是否做参数类型检查?
void g(base & b){
b.play;
}
void main(){
son s;
g(s);
return;
}
大唐电信
DTT笔试题
考试时间一小时,第一部分是填空和选择:
1.数列6,10,18,32,“?”,问“?”是几?
2.某人出70买进一个x,80卖出,90买回,100卖出,这桩买卖怎么样?
3.月球绕地球一圈,至少要多少时间?
4.7个人用7小时挖了7米的沟,以同样的速度在50小时挖50米的沟要多少人?
5.鱼头长9,鱼尾等于鱼头加半个鱼身,鱼身等于鱼头加鱼尾,问鱼全长多少?
6.一个小姐买了一块手表,回家发现手表比她家的表慢了两分钟,晚上看新闻的时候
又发现她家的表比新闻里的时间慢了两分钟,则 。
A 手表和新闻里的时间一样
B 手表比新闻里的时间慢
C 手表比新闻里的时间快
7.王先生看到一则招聘启事,发现两个公司除了以下条件不同外,其他条件都相同
A 半年年薪50万,每半年涨5万
B 一年年薪100万,每一年涨20万
王先生想去一家待遇比较优厚的公司,他会去哪家?
10.问哪个袋子里有金子?
A袋子上的标签是这样写的:B袋子上的话是对的,金子在A袋子。
B袋子上的标签是这样写的:A袋子上的话是错的,金子在A袋子里。
11.3个人住酒店30块钱,经理找回5块钱,服务生从中藏了2块钱,找给每人1块钱,
3×(101)+2=29,问这是怎么回事?
12.三篇写作,均为书信形式。
(1)一片中文的祝贺信,祝贺某男当了某公司xx
(2)两篇英文的,一是说有事不能应邀,派别人去;另一篇是讨债的,7天不给钱就
走人(主要考business letter格式)。
大唐面试试题
1.什么是中断?中断发生时CPU做什么工作?
2.CPU在上电后,进入操作系统的main()之前必须做什么工作?
3.简述ISO OSI的物理层Layer1,链路层Layer2,网络层Layer3的任务。
4.有线电话和无线电话有何区别?无线电话特别需要注意的是什么?
5.软件开发五个主要step是什么?
6.你在开发软件的时候,这5个step分别占用的时间百分比是多少?
7.makefile文件的作用是什么?
8.UNIX显示文件夹中,文件名的命令是什么?能使文件内容显示在屏幕的命令是什么 ?
9.(选做)手机用户在从一个基站漫游到另一个基站的过程中,都会发生什么?
网通笔试题
选择题(每题5分,只有一个正确答案)
1.中国1号信令协议属于 的协议。
A ccs B cas C ip D atm
2.isdnpri协议全称是 。
A 综合业务模拟网基速协议
B 综合业务模拟网模拟协议
C 综合业务数字网基率协议
D 综合业务数字网基次协议
3.路由协议中, 协议是用距离作为向量的。
A ospf B bgp C is-is D rip
4.中国智能网中,ssp与scp间最上层的ss7协议是 。
A incs B is41b C is41c D inap
5.dtmf全称是 。
A 双音多频 B多音双频 C多音三频 D三音多频
6.计算机的基本组成部分中,不包含下面设备的是 。
A cpu B输入设备 C存储器 D接口
7.脉冲编码调制的简称是 。
A pcm B pam C (delta)M D atm
8.普通电话线接口专业称呼是 。
A rj11 B rj45 C rs232 D bnc
9.现有的公共数据网都采用 。
A电路交换技术 B报文交换技术
C语音插空 D分组交换
10.ss7协议中的制止市忙消息简写为 。
A stb B slb C sub D spb
简答题(每题10分)
1.简述普通电话与IP电话的区别。
2.简述随路信令与公路信令的根本区别。
3.说明掩码的主要作用。
4.ss7协议中,有三大要素决定其具体定位,哪三大要素?
5.描述ss7的基本通话过程。
6.简述通信网的组成结构。
7.面向连接与面向非连接各有何利弊?
8.写出爱尔兰的基本计算公式。
9.数据网主要有哪些设备?
10.中国一号协议是如何在被叫号码中插入主叫号码的?
东信笔试题目
笔试:30分钟。
1.压控振荡器的英文缩写。
2.动态随机存储器的英文缩写。
3.选择电阻时要考虑什么?
4.单片机上电后没有运转,首先要检查什么?
5.计算机的基本组成部分及其各自的作用。
6.怎样用D触发器、与或非门组成二分频电路?
中软融鑫笔试题
1.关于工作
(1) 你对未来的工作生活是怎样憧憬的?为何选择我公司作为求职公司?
(2)请用不超过30个字给出一个最能让我们录用你的理由。
(3)你认为比较理想的工作环境是怎样的?
(4)你个人的中长期的职业发展目标是怎样的?
2.关于社会
(1)如果你是杨利伟,你在太空中向祖国人民说的第一句话是什么?
(2)宋美龄女士于2003年10月谢世,对这位著名人士在西安事变中的态度和作用,你
是如何看待的?(不超过300字)
(3)北京政府颁布的对拾金不昧者,失主要奖励相当于财产20%奖金的公告,你是如
何看的?
(4)如果给你50万元人民币,你将会用这些钱做什么?
(5)在美国,男、女卫生间(厕所)的正确称呼为什么?请用英语写出答案。
(6)你认为麦当劳是世界最大的汉堡生产商吗?如果不是,请说出你的观点。
3.教育背景
(1)你受过哪些正规的教育或培训?(自高中毕业起)
(2)在校期间进行过哪些社会活动?
Delphi笔试题目
机械类笔试试题
1. Briefly describe what is blanking(cutting), forming, coining and
emboss
ing in stamping process.
2. What is metal clading?
3. What is the purpose of adding glass fiber to thermoplastic material?
4. In contrast with metal and thermoplastic material,which has a higher
co
efficient of thermal expansion(CTE).
5. The most suitable material for a integral hinge design (typical
plastic
thickness=0.25 to 0.5mm at hinge)
6. Can a bending load makes both compressive and tensile stress in a
membe
r?
7. What is the design criteria used in plastics catch/snap?
8. What is FEA?
9. Why is natural frequency important in vibration analysis?
10. What is the deflection equation of a cantilever beam fixed at one
edge
?
EE笔试试题
1. Name 3 Vehicle Buses.
2. Name 2 possible sources of Electromagnetic interference on
Electronics
Circuit ASM.
3. Wavelength for 12MHz frequency signal is____
4. Name 2 important considerations for car radio performan -ce related
to
audio signal processing under multipath condition?
5. What is the typical FM receiver RF signal strength to achieve 30dB
S/N
for car radio?
6. When a radio is tuned to 98.1 MHz & with a LO of 108.8 MHz, what is
the
image frequency?
7. For a system with a matched impedance, what is the Reflection
Coefficie
nt and SWR?
8. Which property of the output capacitor is the primary cause of Low
Drop
Out(LDO) regulator loop instability?
(1)Equivalent series resistance(ESR)
(2)Effective series inductance(ESL)
(3)Capacitance value
(4)Dielectric material
9. The switching regulator is capable of:
(1)Higher power conversion efficiency
(2)Providing an output voltage that is higher than the input
(3)Generating an output boltage oppsite in polarity to the input
(4)All of the above
10. A linear regulator op Vin(max) = 10v, Vout(min) = 4.8v, Iout(max) =
2.
5mA, Iq(max) = 2.5mA, Ta(max) = 8.5摄氏度,The regulator is available in 3
pac
kages.Each package has the following thermal characteristics:
Package Rja(摄氏度/W) Rjc(摄氏度/W)
SO14 125 30
D1P8 100 52
Choose the most suitable package to handle the power dissipation
requireme
nt without a heat sink and why.
软件笔试题
1. How do you code an infinite loop in C?
2. Volatile:
(1)What does the keyword volatile mean? Give an example
(2)Can a parameter be both const and volatile? Give an example
(3)Can a pointer be volatile? Give an example
3. What are the values of a, b, and c after the following instructions:
int a=5, b=7, c;
c = a+++b;
4. What do the following declarations mean?
(1)const int a;
(2)int const a;
(3)const int *a;
(4)int * const a;
(5)int const * a const;
5. Which of the following statements describe the use of the keyword
stati
c?
(1)Within the body of a function: A static variable maintains its
value
between function revocations
(2)Within a module: A static variable is accessible by all functions
wit
hin that module
(3)Within a module: A static function can only be called by other
functi
ons within that module
6. Embedded systems always require the user to manipulate bits in
register
s or variables. Given an integer variable a, write two code fragments.
The first should set bit 5 of a. The second shnuld clear bit 5 of a. In
bo
th cases, the remaining bits should be unmodified.
7. What does the following function return?
char foo(void)
{
unsigned int a = 6;
iht b = -20;
char c;
(a+b > 6) ? (c=1): (c=0);
return c;
}
8. What will be the output of the following C code?
main()
{
int k, num= 30;
k =(num > 5 ? (num <=10 ? 100:200): 500);
printf(“%d”, k);
}
9. What will the following C code do?
int *ptr;
ptr =(int *)Ox67a9;
*ptr = Oxaa55;
10. What will be the output of the follow C code?
#define product(x) (x*x)
main()
{
int i = 3, j, k;
j = product(i++);
k = product(++i);
printf(“%d %d”,j,k);
}
11. Simplify the following Boolean expression
!((i ==12) || (j > 15))
12. How many flip-flop circuits are needed to divide by 16?
13. Provides 3 properties that make an OS, a RTOS?
14. What is pre-emption?
15. Assume the BC register value is 8538H, and the DE register value is
62
A5H.Find the value of register BC after the following assembly operations:
MOV A,C
SUB E
MOV C,A
MOV A,B
SBB D
MOV B,A
16. In the Assembly code shown below
LOOP: MVI C,78H
DCR C
JNZ LOOP
HLT
How many times is the DCR C Operation executed?
17. Describe the most efficient way (in term of execution time and code
si
ze) to divide a number by 4 in assembly language
18. what value is stored in m in the following assembly language code
frag
ment if n=7?
LDAA #n
LABEL1: CMPA #5
BHI L3
BEQ L2
DECA
BRA L1
LABEL2: CLRA
LABEL3: STAA #m
19. What is the state of a process if a resource is not available?
#define a 365*24*60*60
20. Using the #define statement, how would you declare a manifest
constant
that returns the number of seconds in a year? Disregard leap years in your
an
swer.
21. Interrupts are an important part of embedded systems. Consequently,
ma
ny compiler vendors offer an extension to standard C to support interrupts.
Ty
pically, the keyword is __interrupt. The following routine (ISR). Point out
pr
oblems in the code.
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf(“\nArea = %f”, area);
return area;
}
Hongkong Bank笔试题
1. Please state why you chose to follow these activities and how they
have
contributed to your personal development. You may wish to give details of
you
r role whether anyone else was involved and any difficulties you
encountered.
2. Please state how you have benefited from your work experience.
3. How much is your present monthly salary including allowances.
4. Do you need to compensate your present employer if you resign? If so,
p
lease give details.
5. Other than academic success, what has been your greatest achievement
to
date? What do you see as your personal strength, why?
6. Please state why the position you have applied for is appropriate for
y
ou; Why you have selected HongKong Bank and what your career objectives are.
A.T. Keaney笔试题
1. Describe your greatest achievement in the past 4-5 years?
2. What are your short-term and long-term career objectives? What do you
t
hink is the most ideal job for you?
3. Why do you want to join A.T kearney? What do you think you can
contribu
te to A.T kearney?
4. Why are you applying for a position at Arthur Anderson?
5. What are your expectations of our firm.
6. Describe your hobbies and interests.
Shell company笔试题
1. How wold your colleagues/classmates describe you in five words? On
what
evidence would they base this assessment.
2. If you are asked to recruit the best graduates for shell, what would
yo
u do to attract them? What would you do to select them?
3. Please describe a new activity that you have initiated and
implemented.
Please highlight your role out.
4. Please describe your outstanding non-academic achieve- ments.
5. Please describe any other significant activities you have been
involved
in including organizing people.
6. Imagine that Shell has found oil in an inland province of China, near
a
large river. You are responsible for planning how to transport the oil to
the
coast thousands of miles away. What are the main issue you would consider,
an
d what would you do?
KPMG笔试题
“The big economic difference between nuclear and fossil-fuelled power
sta
tions is that nuclear reactors are more expensive to build and decommission,
b
ut cheaper to sun. So disputes over the relative efficiency of the two
systems
revolve not just around prices of coal and uranium today and tomorrow, but
al
so around the way in which future income should be compared with current
incom
e.”
1. The main difference between nuclear and fossil-fuelled power stations
i
s an economic one.
TRUE
UNTRUE
CANNOT SAY
2. The price of coal is not relevant to discussions about the relative
eff
iciency of nuclear reactors.
TRUE
UNTRUE
CANNOT SAY
3. If nuclear reactors were cheaper to build and decommission than
fossil-
fuelled power stations, they would definitely have the economic advantage.
TRUE
UNTRUE
CANNOT SAY
“At any given moment we are being bombarded by physical and
psychological
stimuli competing for our attention. Although our eyes are capable of
handlin
g more than 5 million bits of data per second, our brain are capable of
interp
reting only about 500 bits per second. With similar disparities between each
o
f the other senses and the brain, it is easy to see that we must select the
vi
sual, auditory, or tactile stimuli that we wish to compute at any specific
tim
e.”
4. Physical stimuli usually win in the competition for our attention.
TRUE
UNTRUE
CANNOT SAY
5. The capacity of the human brain is sufficient to interpret nearly all
t
he stimuli the senses can register under optimum conditions.
TRUE
UNTRUE
CANNOT SAY
6. Eyes are able to cope with a greater input of information than ears.
TRUE
UNTRUE
CANNOT SAY
VERBAL ANSWER:
(1)C CANNOT SAY
(2)B UNTRUE
(3)A TRUE
(4)C CANNOT SAY
(5)B UNTRUE
(6)C CANNOT SAY
PartII NUMERCAL TEST
1.Which country had the highest number of people aged 60 or over at the
s
tart of 1985?
A. UK
B. France
C. Italy
D. W.Germany
E. Spain
2.What percentage of the total 15mm button production was classed as
sub-
standard in September?
AA 10.5% BB 13% CC 15% DD 17.5% EE 20% AB 23.5% AC 25%
AD 27.5% AE 28% BC 30.5%
3. How many live births occurred in 1985 in Spain and Italy together (to
t
he nearest 1000)?
A. 104 000
B. 840 000
C. 1 044 000
D. 8 400 000
E. 10 440 000
4. What was the net effect on the UK population of the live birth and
deat
h rates in 1985?
A. Decrease of 66 700
B. Increase of 752 780
C. Increase of 84 900
D. Cannot Say
E. Increase of 85 270
5. By how much did the total sales value of November‘s button
production
vary from October‘s?
A. 8.50 (Decrease)
B. 42.50 (Decrease)
C. 85.00 (Increase)
D. 27.50 (Decrease)
E. No change
6. What was the loss in potential sales revenue attributable to the
produc
tion of sub-standard (as opposed to standard) buttons over the 6 month
period?
A. 13.75
B. 27.50
C. 137.50
D. 280.00
E. 275.00
香港电信笔试题
1. Based on your understanding of the following java related
technologies:
servlets, JavaServerPage, JavaBeans, Enterprise JavaBeans, how do you think
t
hese technologies are work together or are applied in the development of an
in
ternet-based application (25marks).
2. In your opinion ,what do you think are the advantages or benefitsof
usi
ng an object-oriented approach to software development? how do you think
those
benefits can be achieved or realized? (15marks).
3. In designing your classes, given the choice between inheritance and
agg
regation which do you choose (15marks).
4. How would you work around the lack of multiple inheritance feature in
J
ava (15marks).
5. What would you consider to be the hardest part of OO analysis and
desig
n and why (10marks).
6. How do you keep yourself up to date with the latest in software
techono
gy, especially in the field of software development (10marks).
7. What si your career aspiration? Why do you think this E-Commerce
Develo
pment Center can help you in achieving your career goals (10marks) (1hr,
answe
r in English).
L\‘ORÉAL的笔试题
1. Would you please describe yourself in 3-4 lines? (limited in 500
words)
2. Could you tell us why we should choose you as a Loreal Person, and
what
makes you unique? (limited in 500 words)
3. What is your short-term and long-term career plan? (limited in 500
word
s)
4. What kind of group activities are you interested in and what type of
ro
le do you often play? (limited in 500 words)
5. Please use one sentence to give a definition of ‘Beauty’, and
describ
e the most beautiful thing in your life. (limited in 500 words)
维尔VERITAS软件笔试题
1. A class B network on the internet has a subnet mask of 255.255.240.0,
w
hat is the maximum number of hosts per subnet .
a. 240 b. 255 c. 4094 d. 65534
2. What is the difference: between o(log n) and o(log n^2), where both
log
arithems have base 2 .
a. o(log n^2) is bigger b. o(log n) is bigger
c. no difference
3. For a class what would happen if we call a class’s constructor from
wi
th the same class’s constructor .
a. compilation error b. linking error
c. stack overflow d. none of the above
4. “new” in c++ is a: .
a. library function like malloc in c
b. key word c. operator
d. none of the above
5. Which of the following information is not contained in an inode .
a. file owner b. file size
c. file name d. disk address
6. What’s the number of comparisons in the worst case to merge two
sorted
lists containing n elements each .
a. 2n b.2n-1 c.2n+1 d.2n-2
7. Time complexity of n algorithm T(n), where n is the input size ,is T
(n)
=T(n-1)+1/n if n>1 otherwise 1 the order of this algorithm is .
a. log (n) b. n c. n^2 d. n^n
8. The number of 1’s in the binary representation of 3*4096+
15*256+5*16+
3 are .
a. 8 b. 9 c. 10 d. 12
百威啤酒(武汉公司)
1,为什么申请来百威?
2,将来有什么打算?
3,有没有社会活动经历?
4,有没有当众演讲的经历?
5,经常使用那些软件?
6,喜欢哪些课程?
7,你认为工作中的什么因素对你来说最重要?
8,什么时候可以来上班?可以在这里工作多久?
9,八点上班,要加班和出差,能不能做到?
星巴克
1、 您是一家咖啡店的店经理,你发现店内同时出现下列状况:
1)许多张桌子桌面上有客人离去后留下的空杯未清理,桌面不干净待整理。
2)有客人正在询问店内卖哪些品种,他不知如何点咖啡菜单。
3)已有客人点完成咖啡,正在收银机旁等待结帐。
4)有厂商正准备要进货,需要店经理签收。
请问,针对上述同时发生的情况,你要如何排定处理之先后顺序,为什么
2、 有一位甲员工脾气不好以致在前三家店因为与店内其他同事相处不佳而屡屡调动
,现在甲被调到你的店里面来,请问身为店经理的你,将如何应对??
3、 你是店经理,本周五结帐后,发现门市总销售额较上周五减少30%,请问可能原因
会是哪几种,各原因如何应对?
凹凸电子软件笔试题
1. Select ONE of the following projects to discuss:
a. Signal Filtering: You are given a sampled realtime waveform
consisting
of a sensor reading mixed with highly periodic impulses and high frequency
noi
se.The desired output is the realtime filtered sensor signal with the
impulses
and noise removed, and a readout of the impulse period. The FFT may not be
us
ed.
b. Interrupt Processing.A headware register consisting of eight
independen
t edge triggered latches is used to record external asynchronous interrupt
req
uests. When any of the request bits are latched, a software interrupt is
gener
ated. The software may read the latch to see which interrupt(s) occurred.
Writ
ing a one to any latch bit will clear the latch. How does that software
assure
that no interrupt request is ever missed?
c. User Interface: a prototype MP3 player interface consisting of a
playli
st display and a few control buttons is given to you. How would you make the
i
nterface “skinnable”,with user selected graphics, options, and control
butto
n placement?
Each project description is incomplete. What questions would you ask to
co
mpletely specify the project? What development tools would you prefer to
use?
What algorithm /data structures/design would you use?
2. What program(s) have you coded for you own enjoyment (not part of a
sch
ool project,not for pay). What type of software project would you most enjoy
w
orking on?
3. Have you participated in a team programming project? What is the
hardes
t part of programming as a team, as opposed to programming alone?
友立资讯笔试题目
1.一堆鸡蛋,3个3个数剩余2个,5个5个数剩余1个,7个7个数剩余3个,问这堆鸡蛋
最少有多少个?并给出通解。
2.列举五岳,及其所在省份。
3.何为四书。
4.按顺序默写24节气。
5.默写于谦的《吟石灰》。
6.英语翻译约300字。
7.作文一篇:求职有感。
普华永道PWC笔试题目(作文)
1.最近10年来中国媒体的变化。
2.你认为发展汽车产业和公共交通哪个更重要?
3.如何理解风险投资?
4.如何理解广告的消极作用和积极作用?
Avant! 微电子EE笔试题
1.名词解释:VLSI,CMOS,EDA,VHDL,Verilog,HDL,ROM,RAM,DRC,LVS。
2.简述CMOS工艺流程。
3.画出CMOS与非门的电路,并画出波形图简述其功能。
4.画出N沟道增强型MOSFET的剖面图。
5.简述ESD和latch-up的含义。
6.简述三极管与MOS管的区别。
7.简述MOORE模型和MEALY模型。
8.简述堆栈与队列的区别。
奇码数字信息有限公司笔试题
1.画出NMOS的特性曲线(指明饱和区,截至区,线性区,击穿区和C-V曲线)
2.2.2um工艺下,Kn=3Kp,设计一个反相器,说出器件尺寸。
3.说出制作N-well的工艺流程。
4.雪崩击穿和齐纳击穿的机理和区别。
5.用CMOS画一个D触发器(clk,d,q,q-)。
德勤笔试题
五个人来自不同地方,住不同房子,养不同动物,吸不同牌子香烟,喝不同饮料,喜
欢不同食物。根据以下线索确定谁是养猫的人。
(1)红房子在蓝房子的右边,白房子的左边(不一定紧邻)
(2)黄房子的主人来自香港,而且他的房子不在最左边。
(3)爱吃比萨饼的人住在爱喝矿泉水的人的隔壁。
(4)来自北京的人爱喝茅台,住在来自上海的人的隔壁。
(5)吸希尔顿香烟的人住在养马的人右边隔壁。
(6)爱喝啤酒的人也爱吃鸡。
(7)绿房子的人养狗。
(8)爱吃面条的人住在养蛇的人的隔壁。
(9)来自天津的人的邻居(紧邻)一个爱吃牛肉,另一个来自 成都。
(10)养鱼的人住在最右边的房子里。
(11)吸万宝路香烟的人住在吸希尔顿香烟的人和吸“555”香烟的人的中间(紧邻)
(12)红房子的人爱喝茶。
(13)爱喝葡萄酒的人住在爱吃豆腐的人的右边隔壁。
(14)吸红塔山香烟的人既不住在吸健牌香烟的人的隔壁,也不与来自上海的人相邻 。
(15)来自上海的人住在左数第二间房子里。
(16)爱喝矿泉水的人住在最中间的房子里。
(17)爱吃面条的人也爱喝葡萄酒。
(18)吸“555”香烟的人比吸希尔顿香烟的人住的*右。
扬智(科技)笔试题目
软件题目
1. Queue is a useful structure
* What is a queue?
* Write 5 operations or functions, without details, that can be done on
a
queue.
2. Insert a sequence fo keys(24,49,13,20,59,23,90,35) into a data
structur
e, which has no keys initially. Depict the data structure after these
insertio
ns, if it is:
* a heap tree
* an AVL tree
3. * What is a synchronous I/O bus?
* What is an asnchronous I/O bus?
* Compare the advantages and disadvantages of synchronous and a
synchronou
s I/O bus.
4. Explain the following terminology:
* Baud rate
* Handshaking
* Memory mapped I/O
5. Explain the key issues in supporting a real-time operation system for
e
mbedded system.
6. Explain the mapping of visual addresses to real addresses under
paging
by
* direct mapping
* associative mapping
* combined direct/associated mapping
7. Please explain what is “write-back” and “write-through”, and
discus
s the advantage and disadvantage about these two methods.
8. Explain the concept and benefit of threads
9. What is hardware interrupt? What is software interrupt? What is
excepti
on? Please tell me all you know about interrupt.
10. Write a recursive function that tests wether a string is a
palindrome.
A palindrome is s string such as “abcba” or “otto” that reads the same
in
both directions.If you can write this function recursively,you can write an
i
terative version of this function instead.
11.什么是进程(Process)和线程(Thread)?有何区别?
12.MFC和SDK有何区别?
13.IRP是什么?有何作用?
14.Windows 2000操作系统下用户模式和内核模式下编程有何区别?
15.驱动程序的BUFFER能swap到磁盘上去吗?为什么?
16.试编写3个函数实现
(1)建立一个双向链表
(2)插入一个节点
(3)删除一个节点
17.简述Hardware interrupt和software中断的区别,简述其应用。
18.试编写一个函数,计算一个字符串中A的个数。
19.画出其相应流程图并编写一个函数实现一个整数到二进制数的转换,如输入6,输
出110。
20.
(1)编写一个递归函数,删除一个目录。
(2)编写一个非递归函数,删除一个目录。
并比较其性能。
21.附加题:简单叙述编程经历
硬件题目
1.用mos管搭出一个二输入与非门。
2.集成电路前段设计流程,写出相关的工具。
3.解释名词IRQ,BIOS,USB,VHDL,SDR。
4.简述如下Unix命令cp -r, rm,uname。
5.用波形表示D触发器的功能。
6.写异步D触发器的verilog module。
7.What is PC Chipset?
8.用传输门和倒向器搭一个边沿触发器。
9.画状态机,接受1,2,5分钱的卖报机,每份报纸5分钱。
DSP题目
1.H(n)a*h(n1)+b*δ(n)
(1)求h(n)的z变换
(2)该系统是否为稳定系统
(3)写出FIR数字滤波器的差分方程
2.写出下面模拟信号所需的最小采样带宽
(1)模拟信号的频率范围是0~4kHz
(2)模拟信号的频率范围是2~4kHz
3.名词解释
(1)量化误差
(2)直方图
(3)白平衡
(4)MMX
4.写出下面几种格式中用到的压缩技术
(1)JPEG
(2)MPEG2
(3)MP3
高通笔试题
1. Can you describe the trend of wireless mobile communication industry?
(
2000 letters)
2. Compare the major third generation technologies.(2000 letters)
3. Describe the characteristics of Walsh function. Explain how to
generate
Walsh Function. (2000 letters)
4. List factors that will affect the capacity of forward and reverse
links
of a CDMA system. (2000 letters)
5. What are the differences between IS-95 A/B and cdma2000 1X? (2000
lette
rs)
威盛笔试试题
2002年软件笔试题
1.三组程序,找出你认为的错误。
(1)a.c long temp[255];
b.c extern *temp;
(2)a.c long temp[255];
b.c extern temp[256];
(3)a.c long temp[255];
b.c extern temp[];
2.在第一个声明处编译出了奇怪的错误,为什么?
#include <stdio.h>
#include “myfun1.h”
#include “myfun2.h”
int myInt1;
int myInt2;
3.printf(“0x%x”, (&0)[1]); 请问打印了什么?
4.汇编,用ax,bx,cx,dx,求1000×1000/30(四舍五入),结果放在ax中。
5.编最优化Bubble(int *pIntArray,int L),要求:交换元素不能用临时变量,如果
有序需要最优。
6.用任意一种编程语言写n!的算法。
2003 Asic部分
1.一个四级的Mux,其中第二级信号为关键信号,如何改善timing?
2.一个状态机的题目用Verilog实现。
3.Asic中的design flow的实现。
4.用逻辑门画出D触发器。
5.给出某个一般时序电路的图,有Tsetup,Tdelay,Tck>q还有clock的del
ay,写出决定最大时钟的因素,同时给出表达式。
6.用C语言实现统计某个cell在某.v文件调用的次数。
7.Cache的主要部分。
2003 EE笔试题目
1.写出电流公式。
2.写出平板电容公式。
3.电阻R和电容C串联,输入电压为R和C之间的电压,输出电压分别为C上电压和R上电
压,要求绘制这两种电路输入电压的频谱,判断这两种电路何为高通滤波器,何为低通滤
波器。当RC<<T时,给出输入电压波形图,绘制两种电路的输出波形图。
4.给出时域信号,求其直流分量。
5.给出一时域信号,要求写出频率分量,并写出其傅立叶变换级数。当波形经过低通
滤波器滤掉高次谐波而只保留一次谐波时,画出滤波后的输出波形。
6.有一时域信号S=V0sin(2pif0t)+V1cos(2pif1t)+V2sin(2pif3t+90),写出当其通过
低通、带通、高通滤波器后的信号表示方式。
7.给出一差分电路,告诉其输出电压Y+和Y,求共模分量和差模分量。
8.一电源和一段传输线相连(长度为L,传输时间为T),画出终端处波形,考虑传输
线无损耗。给出电源电压波形图,要求绘制终端波形图。
9.求锁相环的输出频率,给了一个锁相环的结构图。
10.给出一个堆栈的结构,求中断后显示结果,主要是考堆栈压入返回地址存放在低
端地址还是高端。
2003 Graphic笔试题目
1.问答题
(1)texture mapping是什么?为什么要用filter?
(2)用float和int表示一个数,比如2,说明优点和缺点。
(3)在MPEG哪部分可以加速硬件?
(4)解释cubic和B-spline的差别,写出各自函数。
(5)写出几个Win API中的OpenGL函数。
(6)说出固定小数表示和浮点小数表示的优缺点。
(7)说出显卡可以优化哪些MPEG中的计算?
(8)说出Bezier和B-Spline曲线的区别。
2.用最简单的方法判断一个数是否是2的指数次幂。
3.S23E8和S10E5两种浮点数表示方法分析,表示0.25写一个类S10E5,实现从S23E8转 换。
4.用模版的方式实现三个量取最大值。
5.题目告诉你IEEE 16和32浮点数表示的规范,要求将0.25分别用IEEE 16和
32表示并写一个C++函数将输入的IEEE 16表示转化为IEEE 32的表示。
6.用C语言写一个函数f(x) x * 0.5要求只能用整数操作。
2003 Software Engineer笔试题
1. Describe x86 PC’s architecture in a diagram cpu,core chipset,
Cache,DR
AM, IO-subsystem, IO-Bus
2. SWI instruction is often called a “supervisor call”, describe the
act
ions in detail
* Save the address of the instruction after the SWI in rl4_svc.
* Save the CPSR in SPSR_svc.
* Enter supervisor mode and disable IRQs.
* Set the PC to 08 and begin executing the instruction there.
3.
* What is PIO operation? advantage and disadvantage?
* DMA operation? advantage and disadvantage?
* Scatter/Gather DMA engine? how does it operate?
4. MP3 decoder related. (a flow chart of decoding is presented)
* advantages of Huffman encoding?
* why the aliasing reduction is necessary?
* analytical expression in mathematics of the IMDCT?
* which block in the flow chart is suitable for the software
implementatio
n and which for the hardware? why?
5. Assembly codes -> C language (about 15 lines).
6. Graduation thesis description.
汉王笔试题
高级研究人员(模式识别、图像处理类)招聘试题
说明:
可能您的专业并不完全符合本试题所涉及的领域。因此,并非所有的问题都需要回答
,您可以只回答你所熟悉和能够回答的问题。允许参考任意的资料,但请独立完成此试题
,我们更欣赏您独立的思考和创新的精神。本试题并非我们录用或者不录用您的惟一依据
。应聘高级研究人员者请回答这部分问题。
1.人工智能与模式识别的研究已有多年,但似乎公认的观点认为它仍然非常困难。试
对你所熟悉的任一方向(如指纹识别、人像识别、语音识别、字符识别、自然语言理解等
)的发展状况进行描述。并设想如果你将从事该方向的研究,你打算如何着手,以建立有
效的识别理论和方法;或者你认为现在的理论和方法有何缺陷,有什么办法来进行改进?
(500字以内即可,不要太长)
2.简述下面任一主题的主要理论框架或主要观点(500字以内即可,不要太长)
(1)David Marr的视觉计算理论框架
(2)格式塔(Gestalt)心理学派的主要观点
(3)Bayes决策理论
(4)人工神经网络中的BP网络、自组织网络和联想记忆网络的主要内容
(5)基因算法
(6)小波分析
(7)目前流行的有损静态图像压缩方法
3.设想你要设计一个算法,检测给定的图像中是否有矩形结构。所要检测的矩形可能
有多种形态,试提出你的算法框架。要求你的算法至少能检测出样本中的矩形,而拒绝其
他的任意非矩形结构。矩形的大小、位置和方向未知,要求你的算法能确定这些参数。
如果你认为这个问题太难而不能解决,请说明理由。
高级软件开发人员招聘试题
说明:
可能您的专业并不完全符合本试题所涉及的领域。因此,并非所有的问题都需要回答
,您可以只回答你所熟悉和能够回答的问题。允许参考任意的资料,但请独立完成此试题
,我们更欣赏您独立的思考和创新的精神。本试题并非我们录用或者不录用您的惟一依据 。
应聘高级软件开发人员者请回答这部分问题。
1.数据的逻辑存储结构(如数组,队列,树等)对于软件开发具有十分重要的影响,
试对你所了解的各种存储结构从运行速度、存储效率和适用场合等方面进行简要地分析。
2.数据库技术是计算机系统中一个非常重要的领域,几乎所有的计算机应用中都或多
或少地用到了数据库。试简要地谈谈数据库设计中应当注意哪些问题,以及如何解决?给
出两种你所熟悉的DBMS,要求一种适用于小型应用,另一种适用于大型应用,给出你做出
选择的理由。
3.某公司的主要业务是提供WWW和E-mail服务,出于安全考虑,该公司要求我公司提
供一套网络指纹登录系统,该系统要求能够利用指纹替代E-mail中常用的密码,并对所提
供的部分网页通过指纹认证后才能访问,请利用你所学过的知识对该系统进行分析设计,
你可以指定网络的配置(包括协议),但必须保证邮件用户既可通过网页(http方式)收
取信件,也可通过Outlook收取信件。请分析该系统的可行性,可行时给出系统结构和主要
的存储结构,指出系统中的难点和解决方法。(假设指纹识别的问题已经解决)
高级硬件开发人员招聘试题
说明:
可能您的专业并不完全符合本试题所涉及的领域。因此,并非所有的问题都需要回答
,您可以只回答你所熟悉和能够回答的问题。允许参考任意的资料,但请独立完成此试题
,我们更欣赏您独立的思考和创新的精神。本试题并非我们录用或者不录用您的惟一依据 。
应聘高级硬件开发人员者请回答这部分问题。
1.下面是一些基本的数字电路知识问题,请简要回答:
(1)什么是Setup和Holdup时间?
(2)什么是竞争与冒险现象?怎样判断?如何消除?
(3)请画出用D触发器实现2倍分频的逻辑电路。
(4)什么是“线与”逻辑?要实现它,在硬件特性上有什么具体要求?
(5)什么是同步逻辑和异步逻辑?
(6)请画出微机接口电路中,典型的输入设备与微机接口逻辑示意图(数据接口、控
制接口、所存器/缓冲器)。
(7)你知道哪些常用的逻辑电平?TTL与COMS电平可以直接互连吗?
2.可编程逻辑器件在现代电子设计中越来越重要,请问:
(1)你所知道的可编程逻辑器件有哪些?
(2)试用VHDL或Verilog,ABLE描述8位D触发器逻辑
3.设想你将设计完成一个电子电路方案。请简述用EDA软件(如PROTEL)进行设计
(
包括原理图和PCB图)到调试出样机的整个过程。在各个环节应注意哪些问题?
北京信威通信技术股份有限公司面试题
1.DSP和通用处理器在结构上有什么不同?请简要画出你熟悉的一种DSP结构图。
2.说说定点DSP和浮点DSP的定义(或者说出他们的区别)。
3.说说你对循环寻址和位反序寻址的理解。
4.请写出【8,7】的二进制补码和二进制偏置码。用Q15表示出0.5和ɦ
85;0.5。
中国国际金融有限公司CICC笔试题
1. Please tell us about an achievement that you are especially proud of
be
cause it was difficult or demanding.
(1)What the objective was?
(2)Why it is important to you?
(3)How you achieved it and the obstacles that you had to overcome in
ord
er to do so?
2. What is your career plan? Three years after graduation, and five
years
after graduation?
3. Why are you interested in investment bank? What other industries do
you
also have interests?
4. Why do you think you can be a qualified investment banker? How can
you
contribute in this industry?
国泰君安笔试题
一列火车上有三个工人,史密斯、琼斯和罗伯特,三人工作为消防员、司闸员和机械
师,有三个乘客与这三人的名字相同。罗伯特住在底特律;司闸员住在芝加哥和底特律中
间的地方;琼斯一年赚2万美金;有一个乘客和司闸员住在一个地方,每年的薪水是司闸员
的3倍整;史密斯台球打得比消防员好;和司闸员同名的乘客住在芝加哥。
请问谁是机械师?
Briny笔试题
1.说出RC振荡器的构成和工作原理。
2.什么是SDH?
3.什么是共模、差模?画出差分电路的结构。
4.a=5; b=6; a+=b++; 执行结果是什么?
5.什么是TDM?什么是CDMA?
6.什么是采样定理?
7.什么是香农定理?
8.计算机的中断有哪几类?
广东北电面试题目
英文笔试题
1. Tranlation (Mandatory)
CDMA venders have worked hard to give CDMA roaming capabilities via the
de
velopment of RUIM-essentially, a SIM card for CDMA handsets currently being
de
ployed in China for new CDMA operator China Unicom. Korean cellco KTF
demonstr
ated earlier this year the ability to roam between GSM and CDMA using such
car
ds.However,only the card containing the user’s service data can roam-not
the
CDMA handset or the user’s number (except via call forwarding).
2. Programming (Mandatory)
Linked list
a. Implement a linked list for integers,which supports the insertafter
(in
sert a node after a specified node) and removeafter (remove the node after a
s
pecified node) methods;
b. Implement a method to sort the linked list to descending order.
3. Debugging (Mandatory)
a. For each of the following recursive methods,enter Y in the answer box
i
f themethod terminaters (assume i=5), Otherwise enter N.
static int f(int i){
return f(i-1)*f(i-1);
}
Ansewr:
static int f(int i){
if(i==0){return 1;}
else {return f(i-1)*f(i-1);}
}
Ansewr:
static int f(int i){
if(i==0){return 1;}
else {return f(i-1)*f(i-2);}
}
Ansewr:
b. There are two errors in the following JAVA program:
static void g(int i){
if(i==1){return;}
if(i%2==0){g(i/2);return;}
else {g(3*i);return;}
}
please correct them to make sure we can get the printed-out result as
belo
w:
3 10 5 16 8 4 2 1
中文笔试题
1.汉译英
北电网络的开发者计划使来自于不同组织的开发者,能够在北电网络的平台上开发圆
满的补充业务。北电网络符合工业标准的开放接口,为补充业务的开展引入了无数商机,
开发者计划为不同层面的开发者提供不同等级的资格,资格的划分还考虑到以下因素:补
充业务与北电网络平台的集合程度,开发者团体与北电网络的合作关系,等等。
2.编程
将整数转换成字符串:void itoa(int,char);
例如itoa(-123,s[])则s=“-123”;
U2合唱团在17分钟内得赶到演唱会场,途中必需跨过一座桥,四个人从桥的同一端出发,
你得帮助他们到达另一端,天色很暗,而他们只有一只手电筒。一次同时最多可以有两人
一起过桥,而过桥的时候必须持有手电筒,所以就得有人把手电筒带来带去,来回桥两端
。手电筒是不能用丢的方式来传递的。四个人的步行速度各不同,若两人同行则以较慢
者 的速度为准。Bono需花1分钟过桥,Edge需花2分钟过桥,Adam需花5分钟过桥,Larry需
花 10分钟过桥。他们要如何在17分钟内过桥呢?(有个同济的学生写文章说他当时在微软
面 试时就是碰到了这道题,最短只能做出在19分钟内过桥,微软的人对他讲这样的结果已
经 是不错的了!)
A点到B点
1和2过去 2分钟 2
2过来 4分钟 2+2=4
10和5过去 14分钟 4+10=14
1过来 15分钟 14+1=15
1和2过去 17分钟 15+2=17
19分钟还很不错????
这是广州本田的试题,大家看一下
广州本田笔试题
1.排序s-m-t-w-t-f-?
2.如果六千,六百,六表示成6606,那么十一千,十一百,十一表示成什么?
3.grass后面加一个词,agent前面加一个单词,组成两个新词,这个词是什么?
4.农场不知道有多少鸡,现有一批饲料,如果卖掉75只鸡饲料够20天用,买进100
只 鸡饲料够用15天,问原来有多少只鸡?
5.6个桶,装着两种液体,一种液体的价格是另外一种的double,桶容量为8,13,
15,17,19,31,有一个美国人,各用了14美元买两种液体,剩下一个桶。问剩下哪
个?
6.篮球场,还剩6秒,差对手4分,没可能追得上,现在有一个暂停,你会怎么指导
球 员去做?
明基面试问题
1.自我介绍(2分钟)。
2.你大学期间最辉煌的一件事是什么?
3.如果你明天去火星呆上300年,今天晚上你最想做的一件事是什么?
网易
1、10个人分成4组 有几种分法?
2、如图:
7 8 9 10
6 1 2 11
5 4 3 12
16 15 14 13
设“1”的坐标为(0,0) “7”的坐标为(-1,-1) 编写一个小程序,使
程 序做到输入坐标(X,Y)之后显示出相应的数字。
3、#include<stdio.h>
//example input and output
//in 1 2 3 out 1 3 1
//in 123456789 2 100 out 123456789 100 21
long mex(long a,long b,long c)
{ long d;
if(b==0) return 0;
if(b==1) return a%c;
d=mex(a,b/2,c); d*=d;这里忘了;d*=mex(a,b%2,c);d%=c;
return d;
}
int main(void)
{ long x,y,z;
while(1)
{ if(scanf(%d %d %d,&x,&y,&z)>3) return 0;
if(x<0) { printf("too small\n");continue;}
if(y<0) { printf("too small\n");continue;}
if(z<1) { printf("too small\n");continue;}
if(y>z) { printf("too big\n");continue;}
if(z>1000000010) {printf("too big\n");continue}
printf(%d %d %d,x,z,mex(x,y,z);
}}
根据这个程序,当已知一个输入,算出输出,如:输入 1 3 1 则输出 1 2 3
输 入 123456789 100 21 输出 123456789 2 100
广州日报
1、填空部分是一些时事题,如:我国有多少网民,三个代表、北京申奥什么的,及
记 者的一些常识性的问题:如我国第一个以写新闻通讯出名的记者是谁?蔡元培曾经夸奖
过 的记者是谁?
2、选择题范围与填空基本一样,包括时政和新闻知识:如深度采访的实质,记者的
职 业道德等。
3、简答题就比较专业:一道是你参加一条高速公路的开通典礼,如何在记者会上发
的 新闻通稿之外写出会上没说的内容。一道是你去一个单位采访,但没有任何该单位的证
件
、邀请函之类东西,你如何骗过门卫混进去。第三道是有几家香水公司都想让你说好
话, 就是做软新闻了,你该如何处理。第四道是A明星与B明星不和,你如何报道A骂B的话而
又 不能让B告你诽谤。
4、写作题是以“今年冬天不太冷”为题任意想象,加叙加议。
5、五道智力测验:如何喝道啤酒杯底部的啤酒、汽车过隧道但高2厘米该怎么办、
你 吃苹果时吃到几条虫最恶心之类,10只点燃的蜡烛,让风吹灭了2只,后来在关窗户前又吹
灭1只。问最后还有几支。
面试官揭秘500强面试题几年来,我遇到三个刁钻的问题。可惜的是,我因没有心理准备而
未能给出令人满意的答复。
(1)你的约会很多吗?(问这个问题的是美国一家防务公司的一名女面试官。)
(2)你今天为什么来这里?(一家投资银行的面试官走进他的办公室,看到我坐在
那 里等他时所问的问题。)
(3)如果此时外面有一艘宇宙飞船着陆,你会走进去吗?如果它可以去任何一个地方
,你会要求它把您带到哪里?(一家投资银行的面试官所问的问题。)——凯利,1999年
5月3日
□建议
这几个问题都具有挑战性,虽然第一个问题看上去有点不同寻常,是一名女性向另一
名女性提出的问题,但你仍要认真作答,让对方感到满意。比较好的回答方式是:“如果
你担心我对私人生活的关注程度大于对工作的关注程度,那么我向你保证,我对工作非常
投入。同样,我努力保持平衡的生活,以各种各样的方式充实我的业余生活。”这既回答
了面试官的问题,也没有暴露自己的隐私。
“你今天为什么来这里”这样的问题给你提供了一个阐述自己对这份工作的热情的
机 会。如果你不是从这句话的表面意思去看,那么这就是一个刁钻的问题。在接受面试
时, 很重要的一点是让自己轻松一点,不要分析每个问题到底是什么意思。想方设法让你的回
答能够拉近你与面试官之间的关系。并表明你作为这个职位的应聘者,有着自己的优势。
“我来这里是要和你讨论一下我应聘某某工作的问题。你愿意同我介绍一下自己的情况吗 ?”
宇宙飞船这个问题问的是你有多大的冒险精神,要回答这个问题,需要根据你对自
己 所应聘的工作的了解好好组织自己的语言。假如这项工作要求你具有创新精神,那么你
可 以说:“是的,我会上去,去见见曾经在这个星球走过的那些最具有创新精神的人,问
问 他们最喜欢用什么方式来让自己尽可能保持创造力。”
■下水道的井盖为什么是圆的?
微软的顾问有时会得到一些特殊待遇,因为在面试时询问他们的问题并不是真的算
算 术。
□范例
理查德·范曼在微软找工作
面试官:现在我们要问一个问题,看看你的创造性思维能力。不要想得太多,运用
日 常生活中的常识,描述一下你的想法。这个问题是,下水道的井盖为什么是圆的?
范曼:它们并不都是圆的,有些是方的,的确有些圆井盖,但我也看过方的,长方的 。
面试官:不过我们只考虑圆形的井盖,他们为什么是圆的?
范曼:如果我们只考虑圆的,那么它们自然是圆的。
面试官:我的意思是,为什么会存在圆的井盖?把井盖设计成圆形的有什么特殊的
意 义吗?
范曼:是有特殊意义,当需要覆盖的洞是圆形时,通常盖子也是圆的。用一个圆形
的 盖子盖一个圆形的洞,这是最简单的办法。
面试官:你能想到一个圆形的井盖比方形的井盖有哪些优点吗?
范曼:在回答这个问题之前,我们先看看盖子下面是什么。盖子下面的洞是圆的,
因 为圆柱形最能承受周围土地的压力。而且,下水道出孔要留出足够一个人通
posted @
2006-10-18 23:08 Jerry Cat 阅读(3288) |
评论 (1) |
编辑 收藏
.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数。
2.写一个函数,将其中的\t都转换成4个空格。
3.Windows程序的入口是哪里?写出Windows消息机制的流程。
4.如何定义和实现一个类的成员函数为回调函数?
5.C++里面是不是所有的动作都是main()引起的?如果不是,请举例。
6.C++里面如何声明const void f(void)函数为C程序中的库函数?
7.下列哪两个是等同的
int b;
A const int* a = &b;
B const* int a = &b;
C const int* const a = &b;
D int const* const a = &b;
8.内联函数在编译时是否做参数类型检查?
void g(base & b){
b.play;
}
void main(){
son s;
g(s);
return;
}
1,程序设计(可以用自然语言来描述,不编程):C/C++源代码中,检查花括弧(是"("与
")","{"与"}")是否匹配,若不匹配,则输出不匹配花括弧所在的行与列。
2,巧排数字,将1,2,...,19,20这20个数字排成一排,使得相邻的两个数字之和为一个素数,且
首尾两数字之和也为一个素数。编程打印出所有的排法。
3,打印一个N*N的方阵,N为每边字符的个数( 3〈N〈20 ),要求最外层为"X",第二层为"Y",从第三层起每层依次打印数字0,1,2,3,...
例子:当N =5,打印出下面的图形:
X X X X X
X Y Y Y X
X Y 0 Y X
X Y Y Y X
X X X X X
1.请你分别画出OSI的七层网络结构图和TCP/IP的五层结构图。
2.请你详细地解释一下IP协议的定义,在哪个层上面?主要有什么作用?TCP与UDP呢?
3.请问交换机和路由器各自的实现原理是什么?分别在哪个层次上面实现的?
4.请问C++的类和C里面的struct有什么区别?
5.请讲一讲析构函数和虚函数的用法和作用。
6.全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?
7.8086是多少位的系统?在数据总线上是怎么实现的?
联想笔试题
1.设计函数 int atoi(char *s)。
2.int i=(j=4,k=8,l=16,m=32); printf(“%d”, i); 输出是多少?
3.解释局部变量、全局变量和静态变量的含义。
4.解释堆和栈的区别。
5.论述含参数的宏与函数的优缺点。
c++最后几个大题目是
1,实现双向链表删除一个节点P,在节点P后插入一个节点,这两个函数。
2,写一个函数将其中的\t都转换成4个空格。
3,windows程序的入口是哪里?写出windows消息机制的流程。
4,如何定义和实现一个类的成员函数为回调函数。
还有前面的几个:
1. class A{
int a;
int b;
}
问的是编译时的default constructor function的问题。
还有一个说,A有其他自己定义的构造函数,问是否还有default constructor function
还是什么来着,记不清乐。
2. c++里面是不是所有的动作都是main()引起的?如果不是,请举例。
3. c++里面如何声明const void f(void)函数为C库函数?(这个我前几天还看来着,
居然就忘记乐, )
对了,还考乐一些关于const的问题
问下列哪两个是等同的
int b;
A const int* a = &b;
B const* int a = &b;
C const int* const a = &b;
D int const* const a = &b;
还有一个是考类的成员函数是 void f() const;型的时候调用的问题。
幸好昨天刚刚看乐这部分的内容,呵呵
内联函数考了一题,问内联函数在编译时是否做参数类型检查。
虚函数也考了一题,不过不难。
class base{
public:
virtual void play(){
cout<<"base";
}
}
class son: public base{
public:
void play(){cout<<"son";}
}
void g(base & b){
b.play;
}
void main(){
son s;
g(s);
return;
}
我所收集的intel比试题&面试题:
(熟悉大公司的题目,并不仅仅是为了进这些公司,而是很多国内公司考察内容都很接近而已.)
2005笔试 :
1。高效的内存管理
2。8皇后问题
面试q:
(2) 编译中的问题:全局变量如int i=5; int*(pf)()=foo; 分别在何时被初始化?设计时候如何具体的实现。
(3) OS相关的问题,内存访问,cache等(包括cache在整个系统中的位置,画出来,并解释)
(4) 解释例如mov ax,100H 这样一条指令的cpu, os, memory等都完成了什么样的工作。
(5) Strlen()的C语言实现,不能使用任何变量。
(6) 编译中display表的一些问题
(7) 一个hash函数,输入随机,现发生冲突,如数据集中在某几条中,问怎样处理hash函数保证高效的访问,怎样实现?
(8) 把Switch()case…语句翻译成三元组。
(9) 一个byte(用C语言实现计数其中1的个数),给出最高效的实现方法。(位域)或者查表最快的;
(10) 上海有多少个加油站?你是怎样解决这一问题?
(11) C语言参数的入栈顺序?为什么这么实现?
(12) 你的最大的优点和缺点分别是什么?
(13) C语言中字符串的翻转,最高效率(时间和空间)的实现?
2004
1. 三个float:a,b,c 问值
(a+b)+c==(b+a)+c
(a+b)+c==(a+c)+b
2. 把一个链表反向填空
3. 设计一个重采样系统,说明如何anti-alias
4. y1(n)=x(2n), y2(n)=x(n/2),问:
如果y1为周期函数,那么x是否为周期函数
如果x为周期函数,那么y1是否为周期函数
如果y2为周期函数,那么x是否为周期函数
如果x为周期函数,那么y2是否为周期函数
5. 如果模拟信号的带宽为5KHZ,要用8K的采样率,怎么办。
4. 某个程序在一个嵌入式系统(200M的CPU,50M的SDRAM)中已经最化了,换到另一个系统
(300M的CPU,50M的SDRAM)中运行,还需要优化吗?
5. x^4+a*x^3+x^2+c*x+d最少需要作几次乘法
6. 什么情况下,sin(x+y)+y ~ ....
7. 下面哪种排序法对12354最快
a quick sort
b.buble sort
c.merge sort
8. 哪种结构,平均来讲,获取一个值最快
a. binary tree
b. hash table
c. stack
1。 pipeline
2。 程序流程图题目
3。 哲学家进餐
4。 32bit,64bit,两个平台上complier,linker,os kernel,library,debuger的性质
5。 const char * vs char const * (?)
6。 GDT and LDT
7。 1+1<<1
8。 Stack性质
9。 ???
10。正方体中压力什么的。。。
大题
1。f[40,400],log10变换
2。ACPI
3。读程序
4。频谱,采样分析
大题
1。写出下列信号的奈亏斯特频率
(1)f(t)=1+cos(2000pait)+sin(4000pait)
(2)f(t)=sin(4000pait)/pait
(3)f(t)=(sin(4000pait)的平方)/pait
2.填程序
把一个计算m^n的程序填充完整
大概的意思是:
有一个全局数组char s[BUFSIZE]
利用这个数组计算,就是每个单元存放计算结果的一位,index小的存放低位,index大
的存放高位
3。有两个线程
void producer()
{
while(1)
{
GeneratePacket();
PutPacketIntoBuffer();
Signal(customer);
}
}
void customer()
{
while(1)
{
WaitForSignal();
if(PacketInBuffer>10)
{
ReadAllPackets();
ProcessPackets();
}
}
}
(1)有没有其他方法可以提高程序的性能
(2)可不可以不使用信号之类的机制来实现上述的功能
4。优化下面的程序
(0)sum=0
(1)I=1
(2)T1=4*I
(3)T2=address(A)-4
(4)T3=T2[T1]
(5)T4=address(B)-4
(6)T5=4*I
(7)T6=T4[T5]
(8)T7=T3*T5
(9)sum=sum+T6
(10)I=I+1
(10)IF I<20 GOTO (2)
1。关于c的main函数
2。15个人循环报数,报到N的出列,找出最后留下的那个人,算法填空题
2。找出一个给出的并行解决方案的错误情况
3。关于GPIO,intel的四种体系结构
选择题10题
有关vc和c,指针,HyporThreading Dual-core等等
看也看不懂的
2003年的
1:概率题。x,y为随机变量,联合概率密度 f(x,y) = intig(0,1)*dx*intig(0,x)*k*d
y,k为常数,求k=? E(xy)=?
注:intig(a,b)为a到b的定积分。
2:概率题。A,B为随机事件,以下哪个正确
A. P(A U B)*p(AB) <= P(A)P(B)
B. P(A U B)*p(AB) >= P(A)P(B)
C. P(A U B)*p(AB) <= P(A) + P(B)
D. P(A U B)*p(AB) >= P(A) + P(B)
3: 信道带宽200kHz,信噪比10dB,求信道波特率=?
4:以下代码运行结果是什么
int main()
{
int a,b,c,abc = 0;
a=b=c=40;
if(c)
{
int abc;
abc = a*b+c;
}
printf("%d,%d", abc, c);
return 0;
}
5:给出了从纽约出发和到达落山鸡的各种航班信息,写出找到一条从纽约到落山鸡的最
短距离的航班组合的代码。
6:从计算机图形上截取某个物体边缘的若干个坐标,求这个物体面积,并跟判断是方形
还是圆形,为啥。(坐标不记得,大概是个圆
)。
7:离散卷机与DFT的区别与关系。快速求不满足2^N长度的离散傅立叶变换的方法有哪些
?如何用fft求N*M点的离散卷机?
8:给出fir和iir的优缺点。
9:如何计算线性标量量化器的量化噪声?需要那些假设?
1、请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句
2、如何输出源文件的标题和目前执行行的行数
3、两个数相乘,小数点后位数没有限制,请写一个高精度算法
4、写一个病毒
5、有A、B、C、D四个人,要在夜里过一座桥。他们通过这座桥分别需要耗时1、2、5、10分钟,只有一支手电,并且同时最多只能两个人一起过桥。请问,如何安排,能够在17分钟内这四个人都过桥?
2005年腾讯招聘
选择题(60)
c/c++ os linux 方面的基础知识 c的Sizeof函数有好几个!
程序填空(40)
1.(20) 4空x5
不使用额外空间,将 A,B两链表的元素交叉归并
2.(20) 4空x5
MFC 将树序列化 转存在数组或 链表中!
1.请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句
// 这样转向定义应该不算违规吧!^_^
#include
"stdafx.h"
#include
<string.h>
#include
<iostream>
using
namespace
std;
#define
Cmp(x,y) compare(x,y)
int
compare(
int
a,
int
b)
{
a^=(1<<31); b^=(1<<31);
int
i=31;
while
((i^-1) && !((a&(1<<i))^(b&(1<<i)))) i--;
return
(i^-1)?(((a>>i)&1)?1:-1):0;
}
int
_tmain()
{
int
c;
c = Cmp(5,4);
cout<<c<<endl;
return
0;
}
jruv (~~~一叶落而知天下秋~~~) 的答案:
#define COMPARE(a,b) ((a)-(b)) //<0: a<b =0:a==b>0:a>b
2.如何输出源文件的标题和目前执行行的行数
cout << "Filename " << __FILE__ << " Line " << __LINE__ << endl;
3.两个数相乘,小数点后位数没有限制,请写一个高精度算法
算法提示:
输入 string a, string b; 计算string c=a*b; 返回 c;
1,
纪录小数点在a,b中的位置l1,l2, 则需要小数点后移动位置数为l=length(a)+length(b)-l1-l2-2;
2,
去掉a,b中的小数点,(a,b小数点后移,使a,b变为整数)
3,
计算c=a*b; (同整数的大数相乘算法)
4,
输出c,(注意在输出倒数第l个数时,输出一个小数点。若是输出的数少于l个,就补0)
du51(郁郁思扬)的答案:
变为整数求就行了.输入的时候记一下,小数点位置..输出再做点文章就行了.
下面的是大整数的运算.
#include<iostream>
using namespace std;
#define MAX 10000
struct Node{
int data;
Node *next;
};
void output(Node *head)
{
if(!head->next&&!head->data)return;
output(head->next);
cout<<head->data;
}
void Mul(char *a,char *b,int pos)
{
char *ap=a,*bp=b;
Node *head=0;
head=new Node;head->data=0,head->next=0; //头
Node *p,*q=head,*p1;
int temp=0,temp1,bbit;
while(*bp) //若乘数不为空 ,继续.
{
p=q->next;p1=q;
bbit=*bp-48; //把当前位转为整型
while(*ap||temp) //若被乘数不空,继续
{
if(!p) //若要操作的结点为空,申请之
{
p=new Node;
p->data=0;
p->next=0;
p1->next=p;
}
if(*ap==0)temp1=temp;
else { temp1=(p1->data)+(*ap-48)*bbit+temp;ap++; }
p1->data=temp1%10; //留当前位
temp=temp1/10; //进位以int的形式留下.
p1=p;p=p->next; //被乘数到下一位
}
ap=a;bp++;q=q->next; //q进下一位
}
p=head;
output(p); //显示
cout<<endl;
while(head) //释放空间
{
p=head->next;
delete head;
head=p;
}
}
int main()
{
cout<<"请输入两个数"<<endl;
char test1[MAX],test2[MAX];
cin.getline(test1,MAX,'\n');
cin.getline(test2,MAX,'\n');
Mul(strrev(test1),strrev(test2));
system("PAUSE");
return 0;
}
上面大整数已经写了.你加几个东西就行了.
#include<iostream>
using namespace std;
#define MAX 10000
struct Node{
int data;
Node *next;
};
void output(Node *head,int pos)
{
if(!head->next&&!head->data)return;
output(head->next,pos-1);
cout<<head->data;
if(!pos)cout<<".";
}
void Mul(char *a,char *b,int pos)
{
char *ap=a,*bp=b;
Node *head=0;
head=new Node;head->data=0,head->next=0; //头
Node *p,*q=head,*p1;
int temp=0,temp1,bbit;
while(*bp) //若乘数不为空 ,继续.
{
p=q->next;p1=q;
bbit=*bp-48; //把当前位转为整型
while(*ap||temp) //若被乘数不空,继续
{
if(!p) //若要操作的结点为空,申请之
{
p=new Node;
p->data=0;
p->next=0;
p1->next=p;
}
if(*ap==0)temp1=temp;
else { temp1=(p1->data)+(*ap-48)*bbit+temp;ap++; }
p1->data=temp1%10; //留当前位
temp=temp1/10; //进位以int的形式留下.
p1=p;p=p->next; //被乘数到下一位
}
ap=a;bp++;q=q->next; //q进下一位
}
p=head;
output(p,pos); //显示
cout<<endl;
while(head) //释放空间
{
p=head->next;
delete head;
head=p;
}
}
int main()
{
cout<<"请输入两个数"<<endl;
char test1[MAX],test2[MAX],*p;
int pos=0;
cin.getline(test1,MAX,'\n');
cin.getline(test2,MAX,'\n');
if(p=strchr(test1,'.'))
{
pos+=strlen(test1)-(p-test1)-1;
do
{
p++;
*(p-1)=*p;
}while(*p);
}
if(p=strchr(test2,'.'))
{
pos+=strlen(test2)-(p-test2)-1;
do
{
p++;
*(p-1)=*p;
}while(*p);
}
Mul(strrev(test1),strrev(test2),pos);
system("PAUSE");
return 0;
}
4.写一个病毒
cout<<"一个病毒"<<endl;
(开玩笑的,没搞过,^_^)
5.让你在100000000个浮点数中找出最大的10000个,要求时间复杂度优。
//本算法使用快排,O(n*lg(n))
//最低可以找到线性算法,使用预先区域统计划分!类试于构造Quad Trees! 写起来代码会长些!
#include
<stdio.h>
#include
<stdlib.h>
#define
Max 100000000
int
a[Max+10];
int
cmp(
const
void
*a,
const
void
*b)
{
int
*x = (
int
*) a;
int
*y = (
int
*) b;
return
*x-*y;
}
int
main()
{
int
n=0;
while
(scanf("%d",&a[n])==1) n++;
qsort(a,n,4,cmp);
for
(
int
i=0;i<3;i++) printf("%d",a[ i ]);
return
1;
}
5
、有
A
、
B
、
C
、
D
四个人,要在夜里过一座桥。他们通过这座桥分别需要耗时
1
、
2
、
5
、
10
分钟,只有一支手电,并且同时最多只能两个人一起过桥。请问,如何安排,能够在
17
分钟内这四个人都过桥?
Solution:关键是时间最长的两个人必须同时过桥
The First Time
:
A(1)
和
B(2)
过桥,
A(1)
返回
Cost
:
1+2
The Second Time
:
C(5)
和
D(10)
过桥,
B(2)
返回
Cost
:
10+2
The Third Time A(1)
和
B(2)
过桥
Cost
:
2
Total Time Cost
:
(1+2)+(10+2)+2=17 minutes
1.请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句
2.如何输出源文件的标题和目前执行行的行数
3.两个数相乘,小数点后位数没有限制,请写一个高精度算法
4.写一个病毒
posted @
2006-10-18 23:03 Jerry Cat 阅读(5090) |
评论 (1) |
编辑 收藏
windows核心编程--线程的同步
线程的同步
由于同一进程的所有线程共享进程的虚拟地址空间,并且线程的中断是汇编语言级的,所以可能会发生两个线程同时访问同一个对象(包括全局变量、共享资源、API函数和MFC对象等)的情况,这有可能导致程序错误。属于不同进程的线程在同时访问同一内存区域或共享资源时,也会存在同样的问题。因此,在多线程应用程序中,常常需要采取一些措施来同步线程的执行。
需要同步的情况包括以下几种:
在多个线程同时访问同一对象时,可能产生错误。例如,如果当一个线程正在读取一个至关重要的共享缓冲区时,另一个线程向该缓冲区写入数据,那么程序的运行结果就可能出错。程序应该尽量避免多个线程同时访问同一个缓冲区或系统资源。
在
Windows 95环境下编写多线程应用程序还需要考虑重入问题。Windows NT是真正的32位操作系统,它解决了系统重入问题。而Windows 95由于继承了Windows 3.x的部分16位代码,没能够解决重入问题。这意味着在Windows 95中两个线程不能同时执行某个系统功能,否则有可能造成程序错误,甚至会造成系统崩溃。应用程序应该尽量避免发生两个以上的线程同时调用同一个Windows API函数的情况。
由于大小和性能方面的原因,
MFC对象在对象级不是线程安全的,只有在类级才是。也就是说,两个线程可以安全地使用两个不同的CString对象,但同时使用同一个CString对象就可能产生问题。如果必须使用同一个对象,那么应该采取适当的同步措施。
多个线程之间需要协调运行。例如,如果第二个线程需要等待第一个线程完成到某一步时才能运行,那么该线程应该暂时挂起以减少对
CPU的占用时间,提高程序的执行效率。当第一个线程完成了相应的步骤后,应该发出某种信号来激活第二个线程。
关键节和互锁变量访问
关键节 (Critical Seciton) 与 mutex 的功能类似,但它只能由同一进程中的线程使用。关键节可以防止共享资源被同时访问。
进程负责为关键节分配内存空间,关键节实际上是一个CRITICAL_SECTION型的变量,它一次只能被一个线程拥有。在线程使用关键节之前,必须调用InitializeCriticalSection函数将其初始化。如果线程中有一段关键的代码不希望被别的线程中断,那么可以调用EnterCriticalSection函数来申请关键节的所有权,在运行完关键代码后再用LeaveCriticalSection函数来释放所有权。如果在调用EnterCriticalSection时关键节对象已被另一个线程拥有,那么该函数将无限期等待所有权。
利用互锁变量可以建立简单有效的同步机制。使用函数InterlockedIncrement和InterlockedDecrement可以增加或减少多个线程共享的一个32位变量的值,并且可以检查结果是否为0。线程不必担心会被其它线程中断而导致错误。如果变量位于共享内存中,那么不同进程中的线程也可以使用这种机制。
原子访问
所谓原子访问,是指线程在访问资源时能够确保所有其他线程都不在同一时间内访问相同的资源。互锁的函数家族:
LONG InterlockedExchangeAdd(
PLONG plAddend,
LONG Increment);
这是个最简单的函数了。只需调用这个函数,传递一个长变量地址,并指明将这个值递增多少即可。但是这个函数能够保证值的递增以原子操作方式来完成。
LONG InterlockedExchange(PLONG plTarget,
LONG lValue);
PVOID InterlockedExchangePointer(PVOID* ppvTarget,
PVOID pvValue);
I n t e r l o c k e d E x c h a n g e和I n t e r l o c k e d E x c h a n g e P o i n t e r能够以原子操作方式用第二个参数中传递的值来取代第一个参数中传递的当前值。
------------------------------以上为用户方式同步,以下为内核方式同步---------------------------------
等待函数
等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止。
DWORD WaitForSingleObject(HANDLE hObject,
DWORD dwMilliseconds);
函数Wa i t F o r M u l t i p l e O b j e c t s与Wa i t F o r S i n g l e O b j e c t函数很相似,区别在于它允许调用线程同时查看若干个内核对象的已通知状态:
DWORD WaitForMultipleObjects(DWORD dwCount,
CONST HANDLE* phObjects,
BOOL fWaitAll,
DWORD dwMilliseconds);
同步对象
同步对象用来协调多线程的执行,它可以被多个线程共享。线程的等待函数用同步对象的句柄作为参数,同步对象应该是所有要使用的线程都能访问到的。同步对象的状态要么是有信号的,要么是无信号的。同步对象主要有三种:事件、 mutex 和信号灯。
事件对象 (Event) 是最简单的同步对象,它包括有信号和无信号两种状态。在线程访问某一资源之前,也许需要等待某一事件的发生,这时用事件对象最合适。例如,只有在通信端口缓冲区收到数据后,监视线程才被激活。
事件对象是用 CreateEvent 函数建立的。该函数可以指定事件对象的种类和事件的初始状态。如果是手工重置事件,那么它总是保持有信号状态,直到用 ResetEvent 函数重置成无信号的事件。如果是自动重置事件,那么它的状态在单个等待线程释放后会自动变为无信号的。用 SetEvent 可以把事件对象设置成有信号状态。在建立事件时,可以为对象起个名字,这样其它进程中的线程可以用 OpenEvent 函数打开指定名字的事件对象句柄。
mutex对象的状态在它不被任何线程拥有时是有信号的,而当它被拥有时则是无信号的。mutex对象很适合用来协调多个线程对共享资源的互斥访问(mutually exclusive)。
线程用 CreateMutex 函数来建立 mutex 对象,在建立 mutex 时,可以为对象起个名字,这样其它进程中的线程可以用 OpenMutex 函数打开指定名字的 mutex 对象句柄。在完成对共享资源的访问后,线程可以调用 ReleaseMutex 来释放 mutex ,以便让别的线程能访问共享资源。如果线程终止而不释放 mutex ,则认为该 mutex 被废弃。
信号灯对象维护一个从 0 开始的计数,在计数值大于 0 时对象是有信号的,而在计数值为 0 时则是无信号的。信号灯对象可用来限制对共享资源进行访问的线程数量。线程用 CreateSemaphore 函数来建立信号灯对象,在调用该函数时,可以指定对象的初始计数和最大计数。在建立信号灯时也可以为对象起个名字,别的进程中的线程可以用 OpenSemaphore 函数打开指定名字的信号灯句柄。
一般把信号灯的初始计数设置成最大值。每次当信号灯有信号使等待函数返回时,信号灯计数就会减 1 ,而调用 ReleaseSemaphore 可以增加信号灯的计数。计数值越小就表明访问共享资源的程序越多。
可用于同步的对象
对象
|
描述
|
变化通知
|
由
FindFirstChangeNotification 函数建立,当在指定目录中发生指定类型的变化时对象变成有信号的。
|
控制台输入
|
在控制台建立是被创建。它是用
CONIN$ 调用 CreateFile 函数返回的句柄,或是 GetStdHandle 函数的返回句柄。如果控制台输入缓冲区中有数据,那么对象是有信号的,如果缓冲区为空,则对象是无信号的。
|
进程
|
当调用
CreateProcess 建立进程时被创建。进程在运行时对象是无信号的,当进程终止时对象是有信号的。
|
线程
|
当调用
Createprocess 、 CreateThread 或 CreateRemoteThread 函数创建新线程时被创建。在线程运行是对象是无信号的,在线程终止时则是有信号的。
|
另外,有时可以用文件或通信设备作为同步对象使用。
事件内核对象
让我们观察一个简单的例子,以便说明如何使用事件内核对象对线程进行同步。下面就是这个代码:
// Create a global handle to a manual-reset, nonsignaled event.
HANDLE g_hEvent;
int WINAPI WinMain(...)
{
//Create the manual-reset, nonsignaled event.
g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
//Spawn 3 new threads.
HANDLE hThread[3];
DWORD dwThreadID;
hThread[0] = _beginthreadex(NULL, 0, WordCount, NULL, 0, &dwThreadID);
hThread[1] = _beginthreadex(NULL, 0, SpellCheck, NULL, 0, &dwThreadID);
hThread[2] = _beginthreadex(NULL, 0, GrammarCheck, NULL, 0, &dwThreadID);
OpenFileAndReadContentsIntoMemory(...);
//Allow all 3 threads to access the memory.
SetEvent(g_hEvent);
...
}
DWORD WINAPI WordCount(PVOID pvParam)
{
//Wait until the file's data is in memory.
WaitForSingleObject(g_hEvent, INFINITE);
//Access the memory block.
...
return(0);
}
DWORD WINAPI SpellCheck(PVOID pvParam)
{
//Wait until the file's data is in memory.
WaitForSingleObject(g_hEvent, INFINITE);
//Access the memory block.
...
return(0);
}
DWORD WINAPI GrammarCheck(PVOID pvParam)
{
//Wait until the file's data is in memory.
WaitForSingleObject(g_hEvent, INFINITE);
//Access the memory block.
...
return(0);
}
当这个进程启动时,它创建一个人工重置的未通知状态的事件,并且将句柄保存在一个全局变量中。这使得该进程中的其他线程能够非常容易地访问同一个事件对象。现在3个线程已经产生。这些线程要等待文件的内容读入内存,然后每个线程都要访问它的数据。一个线程进行单词计数,另一个线程运行拼写检查器,第三个线程运行语法检查器。这3个线程函数的代码的开始部分都相同,每个函数都调用Wa i t F o r S i n g l e O b j e c t,这将使线程暂停运行,直到文件的内容由主线程读入内存为止。
一旦主线程将数据准备好,它就调用S e t E v e n t,给事件发出通知信号。这时,系统就使所有这3个辅助线程进入可调度状态,它们都获得了C P U时间,并且可以访问内存块。注意,这3个线程都以只读方式访问内存。这就是所有3个线程能够同时运行的唯一原因。还要注意,如何计算机上配有多个C P U,那么所有3个线程都能够真正地同时运行,从而可以在很短的时间内完成大量的操作。
如果你使用自动重置的事件而不是人工重置的事件,那么应用程序的行为特性就有很大的差别。当主线程调用S e t E v e n t之后,系统只允许一个辅助线程变成可调度状态。同样,也无法保证系统将使哪个线程变为可调度状态。其余两个辅助线程将继续等待。
已经变为可调度状态的线程拥有对内存块的独占访问权。让我们重新编写线程的函数,使得每个函数在返回前调用S e t E v e n t函数(就像Wi n M a i n函数所做的那样)。这些线程函数现在变成下面的形式:
DWORD WINAPI WordCount(PVOID pvParam)
{
//Wait until the file's data is in memory.
WaitForSingleObject(g_hEvent, INFINITE);
//Access the memory block.
...
SetEvent(g_hEvent);
return(0);
}
DWORD WINAPI SpellCheck(PVOID pvParam)
{
//Wait until the file's data is in memory.
WaitForSingleObject(g_hEvent, INFINITE);
//Access the memory block.
...
SetEvent(g_hEvent);
return(0);
}
DWORD WINAPI GrammarCheck(PVOID pvParam)
{
//Wait until the file's data is in memory.
WaitForSingleObject(g_hEvent, INFINITE);
//Access the memory block.
...
SetEvent(g_hEvent);
return(0);
}
当线程完成它对数据的专门传递时,它就调用S e t E v e n t函数,该函数允许系统使得两个正在等待的线程中的一个成为可调度线程。同样,我们不知道系统将选择哪个线程作为可调度线程,但是该线程将进行它自己的对内存块的专门传递。当该线程完成操作时,它也将调用S e t E v e n t函数,使第三个即最后一个线程进行它自己的对内存块的传递。注意,当使用自动重置事件时,如果每个辅助线程均以读/写方式访问内存块,那么就不会产生任何问题,这些线程将不再被要求将数据视为只读数据。
等待定时器内核对象
等待定时器是在某个时间或按规定的间隔时间发出自己的信号通知的内核对象。它们通常用来在某个时间执行某个操作。
若要创建等待定时器,只需要调用C r e a t e Wa i t a b l e Ti m e r函数:
HANDLE CreateWaitableTimer(
PSECURITY_ATTRIBUTES psa,
BOOL fManualReset,
PCTSTR pszName);
进程可以获得它自己的与进程相关的现有等待定时器的句柄,方法是调用O p e n Wa i t a b l e Ti m e r函数:
HANDLE OpenWaitableTimer(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
当发出人工重置的定时器信号通知时,等待该定时器的所有线程均变为可调度线程。当发出自动重置的定时器信号通知时,只有一个等待的线程变为可调度线程。
等待定时器对象总是在未通知状态中创建。必须调用S e t Wa i t a b l e Ti m e r函数来告诉定时器你想在何时让它成为已通知状态:
BOOL SetWaitableTimer(
HANDLE hTimer,
const LARGE_INTEGER *pDueTime,
LONG lPeriod,
PTIMERAPCROUTINE pfnCompletionRoutine,
PVOID pvArgToCompletionRoutine,
BOOL fResume);
定时器函数外,最后还有一个C a n c e l Wa i t a b l e Ti m e r函数:
BOOL CancelWaitableTimer(HANDLE hTimer);
这个简单的函数用于取出定时器的句柄并将它撤消,这样,除非接着调用S e t Wa i t a b l e Ti m e r函数以便重新设置定时器,否则定时器决不会进行报时。
信标内核对象
信标内核对象用于对资源进行计数。它们与所有内核对象一样,包含一个使用数量,但是它们也包含另外两个带符号的3 2位值,一个是最大资源数量,一个是当前资源数量。最大资源数量用于标识信标能够控制的资源的最大数量,而当前资源数量则用于标识当前可以使用的资源的数量。
信标的使用规则如下:
• 如果当前资源的数量大于0,则发出信标信号。
• 如果当前资源数量是0,则不发出信标信号。
• 系统决不允许当前资源的数量为负值。
• 当前资源数量决不能大于最大资源数量。
下面的函数用于创建信标内核对象:
HANDLE CreateSemaphore(
PSECURITY_ATTRIBUTE psa,
LONG lInitialCount,
LONG lMaximumCount,
PCTSTR pszName);
通过调用O p e n S e m a p h o r e函数,另一个进程可以获得它自己的进程与现有信标相关的句柄:
HANDLE OpenSemaphore(
DWORD fdwAccess,
BOOL bInheritHandle,
PCTSTR pszName);
通过调用R e l e a s e S e m a p h o r e函数,线程就能够对信标的当前资源数量进行递增:
BOOL ReleaseSemaphore(
HANDLE hsem,
LONG lReleaseCount,
PLONG plPreviousCount);
互斥对象内核对象
互斥对象(m u t e x)内核对象能够确保线程拥有对单个资源的互斥访问权。
互斥对象有许多用途,属于最常用的内核对象之一。通常来说,它们用于保护由多个线程访问的内存块。如果多个线程要同时访问内存块,内存块中的数据就可能遭到破坏。互斥对象能够保证访问内存块的任何线程拥有对该内存块的独占访问权,这样就能够保证数据的完整性。
互斥对象的使用规则如下:
• 如果线程I D是0(这是个无效I D),互斥对象不被任何线程所拥有,并且发出该互斥对象的通知信号。
• 如果I D是个非0数字,那么一个线程就拥有互斥对象,并且不发出该互斥对象的通知信号。
• 与所有其他内核对象不同, 互斥对象在操作系统中拥有特殊的代码,允许它们违反正常的规则(后面将要介绍这个异常情况)。
若要使用互斥对象,必须有一个进程首先调用C r e a t e M u t e x,以便创建互斥对象:
HANDLE CreateMutex(
PSECURITY_ATTRIBUTES psa,
BOOL fInitialOwner,
PCTSTR pszName);
通过调用O p e n M u t e x,另一个进程可以获得它自己进程与现有互斥对象相关的句柄:
HANDLE OpenMutex(
DWORD fdwAccess,
BOOL bInheritHandle,
PCTSTR pszName);
一旦线程成功地等待到一个互斥对象,该线程就知道它已经拥有对受保护资源的独占访问权。试图访问该资源的任何其他线程(通过等待相同的互斥对象)均被置于等待状态中。当目前拥有对资源的访问权的线程不再需要它的访问权时,它必须调用R e l e a s e M u t e x函数来释放该互斥对象:
BOOL ReleaseMutex(HANDLE hMutex);
该函数将对象的递归计数器递减1。
互斥对象与关键代码段的比较
就等待线程的调度而言,互斥对象与关键代码段之间有着相同的特性。但是它们在其他属性方面却各不相同。表9 - 1对它们进行了各方面的比较。
表9-1 互斥对象与关键代码段的比较
特性
|
互斥对象
|
关键代码段
|
运行速度
|
慢
|
快
|
是否能够跨进程边界来使用
|
是
|
否
|
声明
|
HANDLE hmtx;
|
CRITICAL_SECTION cs;
|
初始化
|
h m t x = C r e a t e M u t e x(N U L L,FA L S E,N U L L);
|
I n i t i a l i z e C r i t i c a l S e c t i o n ( & e s );
|
清除
|
C l o s e H a n d l e(h m t x);
|
D e l e t e C r i t i c a l S e c t i o n(& c s);
|
无限等待
|
Wa i t F o r S i n g l e O b j e c t(h m t x , I N F I N I T E);
|
E n t e r C r i t i c a l S e c t i o n(& c s);
|
0等待
|
Wa i t F o r S i n g l e O b j e c t Tr y(h m t x , 0);
|
E n t e r C r i t i c a l S e c t i o n(& c s);
|
任意等待
|
Wa i t F o r S i n g l e O b j e c t(h m t x , d w M i l l i s e c o n d s);
|
不能
|
释放
|
R e l e a s e M u t e x(h m t x);
|
L e a v e C r i t i c a l S e c t i o n(& c s);
|
是否能够等待其他内核对象
|
是(使用Wa i t F o r M u l t i p l e O b j e c t s或类似的函数)
|
否
|
线程同步对象速查表
内核对象与线程同步之间的相互关系
对象
|
何时处于未通知状态
|
何时处于已通知状态
|
成功等待的副作用
|
进程
|
当进程仍然活动时
|
当进程终止运行时(E x i t P r o c e s s,Te r m i n a t e P r o c e s s)
|
无
|
线程
|
当线程仍然活动时
|
当线程终止运行时(E x i t T h r e a d,Te r m i n a t e T h r e a d)
|
无
|
作业
|
当作业的时间尚未结束时
|
当作业的时间已经结束时
|
无
|
文件
|
当I / O请求正在处理时
|
当I / O请求处理完毕时
|
无
|
控制台输入
|
不存在任何输入
|
当存在输入时
|
无
|
文件修改通知
|
没有任何文件被修改
|
当文件系统发现修改时
|
重置通知
|
自动重置事件
|
R e s e t E v e n t , P u l s e - E v e n t或等待成功
|
当调用S e t E v e n t / P u l s e E v e n t时
|
重置事件
|
人工重置事件
|
R e s e t E v e n t或P u l s e E v e n t
|
当调用S e t E v e n t / P u l s e E v e n t时
|
无
|
自动重置等待定时器
|
C a n c e l Wa i t a b l e Ti m e r或等待成功
|
当时间到时(S e t Wa i t a b l e Ti m e r)
|
重置定时器
|
人工重置等待定时器
|
C a n c e l Wa i t a b l e Ti m e r
|
当时间到时(S e t Wa i t a b l e Ti m e r)
|
无
|
信标
|
等待成功
|
当数量> 0时(R e l e a s e S e m a p h o r e)
|
数量递减1
|
互斥对象
|
等待成功
|
当未被线程拥有时(R e l e a s e互斥对象)
|
将所有权赋予线程
|
关键代码段(用户方式)
|
等待成功((Tr y)E n t e r C r i t i c a l S e c t i o n)
|
当未被线程拥有时(L e a v e C r i t i c a l S e c t i o n)
|
将所有权赋予线程
|
其他的线程同步函数
1 异步设备I / O使得线程能够启动一个读操作或写操作,但是不必等待读操作或写操作完成。例如,如果线程需要将一个大文件装入内存,那么该线程可以告诉系统将文件装入内存。然后,当系统加载该文件时,该线程可以忙于执行其他任务,如创建窗口、对内部数据结构进行初始化等等。当初始化操作完成时,该线程可以终止自己的运行,等待系统通知它文件已经读取。
2 线程也可以调用Wa i t F o r I n p u t I d l e来终止自己的运行:
DWORD WaitForInputIdle(
HANDLE hProcess,
DWORD dwMilliseconds);
该函数将一直处于等待状态,直到h P r o c e s s标识的进程在创建应用程序的第一个窗口的线程中已经没有尚未处理的输入为止。这个函数可以用于父进程。父进程产生子进程,以便执行某些操作。
3 线程可以调用M s g Wa i t F o r M u l t i p l e O b j e c t s或M s g Wa i t F o r M u l t i p l e O b j e c t s E x函数,让线程等待它自己的消息:
DWORD MsgWaitForMultipleObjects(
DWORD dwCount,
PHANDLE phObjects,
BOOL fWaitAll,
DWORD dwMilliseconds,
DWORD dwWakeMask);
DWORD MsgWaitForMultipleObjectsEx(
DWORD dwCount,
PHANDLE phObjects,
DWORD dwMilliseconds,
DWORD dwWakeMask,
DWORD dwFlags);
这些函数与Wa i t F o r M u l t i p l e O b j e c t s函数十分相似。差别在于它们允许线程在内核对象变成已通知状态或窗口消息需要调度到调用线程创建的窗口中时被调度。
4 Wi n d o w s将非常出色的调试支持特性内置于操作系统之中。当调试程序启动运行时,它将自己附加给一个被调试程序。该调试程序只需闲置着,等待操作系统将与被调试程序相关的调试事件通知它。调试程序通过调用Wa i t F o r D e b u g E v e n t函数来等待这些事件的发生:
BOOL WaitForDebugEvent(
PDEBUG_EVENT pde,
DWORD dwMilliseconds);
当调试程序调用该函数时,调试程序的线程终止运行,系统将调试事件已经发生的情况通知调试程序,方法是允许调用的Wa i t F o r D e b u g E v e n t函数返回。
5 S i n g l e O b j e c t A n d Wa i t函数用于在单个原子方式的操作中发出关于内核对象的通知并等待另一个内核对象:
DWORD SingleObjectAndWait(
HANDLE hObjectToSignal,
HANDLE hObjectToWaitOn,
DWORD dwMilliseconds,
BOOL fAlertable);
posted @
2006-10-13 19:47 Jerry Cat 阅读(525) |
评论 (0) |
编辑 收藏
windows核心编程--线程
进程是由两个部分构成的,一个是进程内核对象,另一个是地址空间。同样,线程也是由两个部分组成的:
• 一个是线程的内核对象,操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。
• 另一个是线程堆栈,它用于维护线程在执行代码时需要的所有函数参数和局部变量
进程从来不执行任何东西,它只是线程的容器。线程总是在某个进程环境中创建的,而且它的整个寿命期都在该进程中。如果在单进程环境中,你有两个或多个线程正在运行,那么这两个线程将共享单个地址空间。这些线程能够执行相同的代码,对相同的数据进行操作。这些线程还能共享内核对象句柄,因为句柄表依赖于每个进程而不是每个线程存在。
线程用于描述进程中的运行路径。每当进程被初始化时,系统就要创建一个主线程。该线程与C / C + +运行期库的启动代码一道开始运行,启动代码则调用进入点函数( m a i n、w m a i n、Wi n M a i n或w Wi n M a i n),并且继续运行直到进入点函数返回并且C / C + +运行期库的启动代码调用E x i t P r o c e s s为止。对于许多应用程序来说,这个主线程是应用程序需要的唯一线程。不过,进程能够创建更多的线程来帮助执行它们的操作。
多线程有很多的好处,能更好地利用cpu,及其他的计算机资源,能够使应用程序界面和后台操作同时进行,提供更加友好的用户接口.但是如果用的不合适的化,会带来不必要的麻烦,例如Windows Explorer为每个文件夹窗口创建了一个独立的线程。它使你能够将文件从一个文件夹拷贝到另一个文件夹,并且仍然可以查看你的系统上的其他文件夹。
CreateThread函数
每个线程必须拥有一个进入点函数,线程从这个进入点开始运行。前面已经介绍了主线程的进入点函数:即m a i n、w m a i n、Wi n M a i n或w Wi n M a i n。如果想要在你的进程中创建一个辅助线程,它必定也是个进入点函数,类似下面的样子:
DWORD WINAPI ThreadFunc(PVOID pvParam)
{
DWORD dwResult = 0;
...
return(dwResult);
}
线程函数必须返回一个值,它将成为该线程的退出代码。这与C / C + +运行期库关于让主线程的退出代码作为进程的退出代码的原则是相似的。
• 线程函数(实际上是你的所有函数)应该尽可能使用函数参数和局部变量。当使用静态变量和全局变量时,多个线程可以同时访问这些变量,这可能破坏变量的内容。然而,参数和局部变量是在线程堆栈中创建的,因此它们不太可能被另一个线程破坏。
前面已经讲述了调用C r e a t e P r o c e s s函数时如何创建进程的主线程。如果想要创建一个或多个辅助函数,只需要让一个已经在运行的线程来调用C r e a t e T h r e a d:
HANDLE CreateThread(
PSECURITY_ATTRIBUTES psa,
DWORD cbStack,
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam,
DWORD fdwCreate,
PDWORD pdwThreadID);
当C r e a t e T h r e a d被调用时,系统创建一个线程内核对象。
系统从进程的地址空间中分配内存,供线程的堆栈使用。新线程运行的进程环境与创建线程的环境相同。因此,新线程可以访问进程的内核对象的所有句柄、进程中的所有内存和在这个相同的进程中的所有其他线程的堆栈。这使得单个进程中的多个线程确实能够非常容易地互相通信。
终止线程的运行
若要终止线程的运行,可以使用下面的方法:
• 线程函数返回(最好使用这种方法)。
• 通过调用E x i t T h r e a d函数,线程将自行撤消(最好不要使用这种方法)。
• 同一个进程或另一个进程中的线程调用Te r m i n a t e T h r e a d函数(应该避免使用这种方法)。
• 包含线程的进程终止运行(应该避免使用这种方法)。
线程函数返回
始终都应该将线程设计成这样的形式,即当想要线程终止运行时,它们就能够返回。这是确保所有线程资源被正确地清除的唯一办法。
如果线程能够返回,就可以确保下列事项的实现:
• 在线程函数中创建的所有C + +对象均将通过它们的撤消函数正确地撤消。
• 操作系统将正确地释放线程堆栈使用的内存。
• 系统将线程的退出代码(在线程的内核对象中维护)设置为线程函数的返回值。
• 系统将递减线程内核对象的使用计数。
ExitThread函数
可以让线程调用E x i t T h r e a d函数,以便强制线程终止运行:
VOID ExitThread(DWORD dwExitCode);
该函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C + +资源(如C + +类对象)将不被撤消。
TerminateThread函数
调用Te r m i n a t e T h r e a d函数也能够终止线程的运行:
BOOL TerminateThread(
HANDLE hThread,
DWORD dwExitCode);
与E x i t T h r e a d不同,E x i t T h r e a d总是撤消调用的线程,而Te r m i n a t e T h r e a d能够撤消任何线程。
注意Te r m i n a t e T h r e a d函数是异步运行的函数,也就是说,它告诉系统你想要线程终止运行,但是,当函数返回时,不能保证线程被撤消。如果需要确切地知道该线程已经终止运行,必须调用Wa i t F o r S i n g l e O b j e c t (第9章介绍)或者类似的函数,传递线程的句柄。此外,当线程终止运行时, D L L通常接收通知。如果使用Terminate Thread 强迫线程终止,D L L就不接收通知,这能阻止适当的清除
在进程终止运行时撤消线程
E x i t P r o c e s s和Te r m i n a t e P r o c e s s函数也可以用来终止线程的运行。差别在于这些线程将会使终止运行的进程中的所有线程全部终止运行。另外,由于整个进程已经被关闭,进程使用的所有资源肯定已被清除。这当然包括所有线程的堆栈。这两个函数会导致进程中的剩余线程被强制撤消,就像从每个剩余的线程调用Te r m i n a t e T h r e a d一样。显然,这意味着正确的应用程序清除没有发生,即C + +对象撤消函数没有被调用,数据没有转至磁盘等等。
线程终止运行时发生的操作
当线程终止运行时,会发生下列操作:
• 线程拥有的所有用户对象均被释放。在Wi n d o w s中,大多数对象是由包含创建这些对象的线程的进程拥有的。但是一个线程拥有两个用户对象,即窗口和挂钩。当线程终止运行时,系统会自动撤消任何窗口,并且卸载线程创建的或安装的任何挂钩。其他对象只有在拥有线程的进程终止运行时才被撤消。
• 线程的退出代码从S T I L L _ A C T I V E改为传递给E x i t T h r e a d或Te r m i n a t e T h r e a d的代码。
• 线程内核对象的状态变为已通知。
• 如果线程是进程中最后一个活动线程,系统也将进程视为已经终止运行。
• 线程内核对象的使用计数递减1。
当一个线程终止运行时,在与它相关联的线程内核对象的所有未结束的引用关闭之前,该内核对象不会自动被释放。
由于线程常常要改变它的(或它的进程的)环境,因此Wi n d o w s提供了一些函数,使线程能够很容易引用它的进程内核对象,或者引用它自己的线程内核对象:
HANDLE GetCurrentProcess();
HANDLE GetCurrentThread();
(还有对应的函数可以得到进程和线程的id)
上面这两个函数都能返回调用线程的进程的伪句柄或线程内核对象的伪句柄。这些函数并不在创建进程的句柄表中创建新句柄。还有,调用这些函数对进程或线程内核对象的使用计数没有任何影响。如果调用C l o s e H a n d l e,将伪句柄作为参数来传递,那么C l o s e H a n d l e就会忽略该函数的调用并返回FA L S E。
有时可能需要获得线程的实句柄而不是它的伪句柄。所谓“实句柄”,我是指用来明确标识一个独一无二的线程的句柄。
线程的伪句柄是当前线程的句柄,也就是说,它是调用函数的线程的句柄。
伪句柄变成实句柄。D u p l i c a t e H a n d l e函数能够执行这一转换:
BOOL DuplicateHandle(
HANDLE hSourceProcess,
HANDLE hSource,
HANDLE hTargetProcess,
PHANDLE phTarget,
DWORD fdwAccess,
BOOL bInheritHandle,
DWORD fdwOptions);
通常可以使用这个函数,用与另一个进程相关的内核对象来创建一个与进程相关的新句柄。
还要指出,D u p l i c a t e H a n d l e可以用来将进程的伪句柄转换成进程的实句柄,如下面的代码所示:
HANDLE hProcess;
DuplicateHandle(
GetCurrentProcess(),
//
Handle of process that the process
//
pseudo-handle is relative to
GetCurrentProcess(),
//
Process's pseudo-handle
GetCurrentProcess(),
//
Handle of process that the new, real,
//
process handle is relative to
&
hProcess,
//
Will receive the new, real
//
handle identifying the process
0
,
//
Ignored because of DUPLICATE_SAME_ACCESS
FALSE,
//
New thread handle is not inheritable
DUPLICATE_SAME_ACCESS);
//
New process handle has same
//
access as pseudo-handle
posted @
2006-10-13 17:10 Jerry Cat 阅读(290) |
评论 (0) |
编辑 收藏
[转]windows核心编程--内存映射文件
与虚拟内存一样,内存映射文件可以用来保留一个地址空间的区域,并将物理存储器提交给该区域。它们之间的差别是,物理存储器来自一个已经位于磁盘上的文件,而不是系统的页文件。一旦该文件被映射,就可以访问它,就像整个文件已经加载内存一样。
内存映射文件可以用于3个不同的目的:
• 系统使用内存映射文件,以便加载和执行. e x e和D L L文件。这可以大大节省页文件空间和应用程序启动运行所需的时间。
• 可以使用内存映射文件来访问磁盘上的数据文件。这使你可以不必对文件执行I / O操作,并且可以不必对文件内容进行缓存。
• 可以使用内存映射文件,使同一台计算机上运行的多个进程能够相互之间共享数据。Wi n d o w s确实提供了其他一些方法,以便在进程之间进行数据通信,但是这些方法都是使用内存映射文件来实现的,这使得内存映射文件成为单个计算机上的多个进程互相进行通信的最有效的方法。
内存映射的可执行文件和DLL文件
当线程调用C r e a t e P r o c e s s时,系统将执行下列操作步骤:
1) 系统找出在调用C r e a t e P r o c e s s时设定的. e x e文件。如果找不到这个. e x e文件,进程将无法创建,C r e a t e P r o c e s s将返回FA L S E。
2) 系统创建一个新进程内核对象。
3) 系统为这个新进程创建一个私有地址空间。
4) 系统保留一个足够大的地址空间区域,用于存放该. e x e文件。该区域需要的位置在. e x e文件本身中设定。按照默认设置, . e x e文件的基地址是0 x 0 0 4 0 0 0 0 0(这个地址可能不同于在6 4位Windows 2000上运行的6 4位应用程序的地址),但是,可以在创建应用程序的. e x e文件时重载这个地址,方法是在链接应用程序时使用链接程序的/ B A S E选项。
5) 系统注意到支持已保留区域的物理存储器是在磁盘上的. e x e文件中,而不是在系统的页文件中。
当. e x e文件被映射到进程的地址空间中之后,系统将访问. e x e文件的一个部分,该部分列出了包含. e x e文件中的代码要调用的函数的D L L文件。然后,系统为每个D L L文件调用L o a d L i b r a r y函数,如果任何一个D L L需要更多的D L L,那么系统将调用L o a d L i b r a r y函数,以便加载这些D L L。每当调用L o a d L i b r a r y来加载一个D L L时,系统将执行下列操作步骤,它们均类似上面的第4和第5个步骤:
1) 系统保留一个足够大的地址空间区域,用于存放该D L L文件。该区域需要的位置在D L L文件本身中设定。按照默认设置, M i c r o s o f t的Visual C++ 建立的D L L文件基地址是0 x 1 0 0 0 0 0 0 0(这个地址可能不同于在6 4位Windows 2000上运行的6 4位D L L的地址)但是,你可以在创建D L L文件时重载这个地址,方法是使用链接程序的/ B A S E选项。Wi n d o w s提供的所有标准系统D L L都拥有不同的基地址,这样,如果加载到单个地址空间,它们就不会重叠。
2) 如果系统无法在该D L L的首选基地址上保留一个区域,其原因可能是该区域已经被另一个D L L或. e x e占用,也可能是因为该区域不够大,此时系统将设法寻找另一个地址空间的区域来保留该D L L。
3) 系统会注意到支持已保留区域的物理存储器位于磁盘上的D L L文件中,而不是在系统的页文件中。
如果由于某个原因系统无法映射. e x e和所有必要的D L L文件,那么系统就会向用户显示一个消息框,并且释放进程的地址空间和进程对象。
当所有的. e x e和D L L文件都被映射到进程的地址空间之后,系统就可以开始执行. e x e文件的启动代码。当. e x e文件被映射后,系统将负责所有的分页、缓冲和高速缓存的处理。
在可执行文件或DLL的多个实例之间共享静态数据 (通过定义共享的节)
全局数据和静态数据不能被同一个. e x e或D L L文件的多个映像共享,这是个安全的默认设置。但是,在某些情况下,让一个. e x e文件的多个映像共享一个变量的实例是非常有用和方便的。例如,Wi n d o w s没有提供任何简便的方法来确定用户是否在运行应用程序的多个实例。但是,如果能够让所有实例共享单个全局变量,那么这个全局变量就能够反映正在运行的实例的数量。
内存映射数据文件
操作系统使得内存能够将一个数据文件映射到进程的地址空间中。因此,对大量的数据进行操作是非常方便的。
为了理解用这种方法来使用内存映射文件的功能,让我们看一看如何用4种方法来实现一个程序,以便将文件中的所有字节的顺序进行倒序。
方法1:一个文件,一个缓存
第一种方法也是理论上最简单的方法,它需要分配足够大的内存块来存放整个文件。该文件被打开,它的内容被读入内存块,然后该文件被关闭。文件内容进入内存后,我们就可以对所有字节的顺序进行倒序,方法是将第一个字节倒腾为最后一个字节,第二个字节倒腾为倒数第二个字节,依次类推。这个倒腾操作将一直进行下去直到文件的中间位置。当所有的字节都已经倒腾之后,就可以重新打开该文件,并用内存块的内容来改写它的内容。
这种方法实现起来非常容易,但是它有两个缺点。首先,必须分配一个与文件大小相同的内存块。如果文件比较小,那么这没有什么问题。但是如果文件非常大,比如说有2 G B大,那该怎么办呢?一个3 2位的系统不允许应用程序提交那么大的物理内存块。因此大文件需要使用不同的方法。
第二,如果进程在运行过程的中间被中断,也就是说当倒序后的字节被重新写入该文件时进程被中断,那么文件的内容就会遭到破坏。防止出现这种情况的最简单的方法是在对它的内容进行倒序之前先制作一个原始文件的拷贝。如果整个进程运行成功,那么可以删除该文件的拷贝。这种方法需要更多的磁盘空间。
方法2:两个文件,一个缓存
在第二种方法中,你打开现有的文件,并且在磁盘上创建一个长度为0的新文件。然后分配一个比较小的内部缓存,比如说8 KB。你找到离原始文件结尾还有8 KB的位置,将这最后的8 KB读入缓存,将字节倒序,再将缓存中的内容写入新创建的文件。这个寻找、读入、倒序和写入的操作过程要反复进行,直到到达原始文件的开头。如果文件的长度不是8 KB的倍数,那么必须进行某些特殊的处理。当原始文件完全处理完毕之后,将原始文件和新文件关闭,并删除原始文件。
这种方法实现起来比第一种方法要复杂一些。它对内存的使用效率要高得多,因为它只需要分配一个8 KB的缓存块,但是它存在两个大问题。首先,它的处理速度比第一种方法要慢,原因是在每个循环操作过程中,在执行读入操作之前,必须对原始文件进行寻找操作。第二,这种方法可能要使用大量的硬盘空间。如果原始文件是400 MB,那么随着进程的不断运行,新文件就会增大为400 MB。在原始文件被删除之前,两个文件总共需要占用800 MB的磁盘空间。这比应该需要的空间大400 MB。由于存在这个缺点,因此引来了下一个方法。
方法3:一个文件,两个缓存
如果使用这个方法,那么我们假设程序初始化时分配了两个独立的8 KB缓存。程序将文件的第一个8 KB读入一个缓存,再将文件的第二个8 KB 读入另一个缓存。然后进程将两个缓存的内容进行倒序,并将第一个缓存的内容写回文件的结尾处,将第二个缓存的内容写回同一个文件的开始处。每个迭代操作不断进行(以8 KB为单位,从文件的开始和结尾处移动文件块)。如果文件的长度不是16 KB的倍数,并且有两个8 KB的文件块相重叠,那么就需要进行一些特殊的处理。这种特殊处理比上一种方法中的特殊处理更加复杂,不过这难不倒经验丰富的编程员。
与前面的两种方法相比,这种方法在节省硬盘空间方面有它的优点。由于所有内容都是从同一个文件读取并写入同一个文件,因此不需要增加额外的磁盘空间,至于内存的使用,这种方法也不错,它只需要使用16 KB的内存。当然,这种方法也许是最难实现的方法。与第一种方法一样,如果进程被中断,本方法会导致数据文件被破坏。
下面让我们来看一看如何使用内存映射文件来完成这个过程。
方法4:一个文件,零缓存
当使用内存映射文件对文件内容进行倒序时,你打开该文件,然后告诉系统将虚拟地址空间的一个区域进行倒序。你告诉系统将文件的第一个字节映射到该保留区域的第一个字节。然后可以访问该虚拟内存的区域,就像它包含了这个文件一样。实际上,如果在文件的结尾处有一个单个0字节,那么只需要调用C运行期函数_ s t r r e v,就可以对文件中的数据进行倒序操作。
这种方法的最大优点是,系统能够为你管理所有的文件缓存操作。不必分配任何内存,或者将文件数据加载到内存,也不必将数据重新写入该文件,或者释放任何内存块。但是,内存映射文件仍然可能出现因为电源故障之类的进程中断而造成数据被破坏的问题。
使用内存映射文件
若要使用内存映射文件,必须执行下列操作步骤:
1) 创建或打开一个文件内核对象,该对象用于标识磁盘上你想用作内存映射文件的文件。
2) 创建一个文件映射内核对象,告诉系统该文件的大小和你打算如何访问该文件。
3) 让系统将文件映射对象的全部或一部分映射到你的进程地址空间中。
当完成对内存映射文件的使用时,必须执行下面这些步骤将它清除:
1) 告诉系统从你的进程的地址空间中撤消文件映射内核对象的映像。
2) 关闭文件映射内核对象。
3) 关闭文件内核对象。
步骤1:创建或打开文件内核对象
若要创建或打开一个文件内核对象,总是要调用C r e a t e F i l e函数:
HANDLE CreateFile(
PCSTR pszFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
PSECURITY_ATTRIBUTES psa,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
步骤2:创建一个文件映射内核对象
调用C r e a t e F i l e函数,就可以将文件映像的物理存储器的位置告诉操作系统。你传递的路径名用于指明支持文件映像的物理存储器在磁盘(或网络或光盘)上的确切位置。这时,必须告诉系统,文件映射对象需要多少物理存储器。若要进行这项操作,可以调用C r e a t e F i l e M a p p i n g函数:
HANDLE CreateFileMapping(
HANDLE hFile,
PSECURITY_ATTRIBUTES psa,
DWORD fdwProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
PCTSTR pszName);
步骤3:将文件数据映射到进程的地址空间
当创建了一个文件映射对象后,仍然必须让系统为文件的数据保留一个地址空间区域,并将文件的数据作为映射到该区域的物理存储器进行提交。可以通过调用M a p Vi e w O f F i l e函数来进行这项操作:
PVOID MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap);
步骤4:从进程的地址空间中撤消文件数据的映像
当不再需要保留映射到你的进程地址空间区域中的文件数据时,可以通过调用下面的函数将它释放:
BOOL UnmapViewOfFile(PVOID pvBaseAddress);
为了提高速度,系统将文件的数据页面进行高速缓存,并且在对文件的映射视图进行操作时不立即更新文件的磁盘映像。如果需要确保你的更新被写入磁盘,可以强制系统将修改过的数据的一部分或全部重新写入磁盘映像中,方法是调用F l u s h Vi e w O f F i l e函数:
BOOL FlushViewOfFile(
PVOID pvAddress,
SIZE_T dwNumberOfBytesToFlush);
步骤5和步骤6:关闭文件映射对象和文件对象
不用说,你总是要关闭你打开了的内核对象。如果忘记关闭,在你的进程继续运行时会出现资源泄漏的问题。当然,当你的进程终止运行时,系统会自动关闭你的进程已经打开但是忘记关闭的任何对象。但是如果你的进程暂时没有终止运行,你将会积累许多资源句柄。因此你始终都应该编写清楚而又“正确的”代码,以便关闭你已经打开的任何对象。若要关闭文件映射对象和文件对象,只需要两次调用C l o s e H a n d l e函数,每个句柄调用一次:
让我们更加仔细地观察一下这个进程。下面的伪代码显示了一个内存映射文件的例子:
HANDLE hFile = CreateFile(...);
HANDLE hFileMapping = CreateFileMapping(hFile, ...);
PVOID pvFile = MapViewOfFile(hFileMapping, ...);
// Use the memory-mapped file.
UnmapViewOfFile(pvFile);
CloseHandle(hFileMapping);
CloseHandle(hFile);
上面的代码显示了对内存映射文件进行操作所用的“预期”方法。但是,它没有显示,当你调用M a p Vi e w O f F i l e时系统对文件对象和文件映射对象的使用计数的递增情况。这个副作用是很大的,因为它意味着我们可以将上面的代码段重新编写成下面的样子:
HANDLE hFile = CreateFile(...);
HANDLE hFileMapping = CreateFileMapping(hFile, ...);
CloseHandle(hFile);
PVOID pvFile = MapViewOfFile(hFileMapping, ...);
CloseHandle(hFileMapping);
// Use the memory-mapped file.
UnmapViewOfFile(pvFile);
当对内存映射文件进行操作时,通常要打开文件,创建文件映射对象,然后使用文件映射对象将文件的数据视图映射到进程的地址空间。由于系统递增了文件对象和文件映射对象的内部使用计数,因此可以在你的代码开始运行时关闭这些对象,以消除资源泄漏的可能性。
如果用同一个文件来创建更多的文件映射对象,或者映射同一个文件映射对象的多个视图,那么就不能较早地调用C l o s e H a n d l e函数——以后你可能还需要使用它们的句柄,以便分别对C r e a t e F i l e M a p p i n g和M a p Vi e w O f F i l e函数进行更多的调用。
使用内存映射文件来处理大文件
使用内存映射文件在进程之间共享数据
Wi n d o w s总是出色地提供各种机制,使应用程序能够迅速而方便地共享数据和信息。这些机制包括R P C、C O M、O L E、D D E、窗口消息(尤其是W M _ C O P Y D ATA)、剪贴板、邮箱、管道和套接字等。在Wi n d o w s中,在单个计算机上共享数据的最低层机制是内存映射文件。不错,如果互相进行通信的所有进程都在同一台计算机上的话,上面提到的所有机制均使用内存映射文件从事它们的烦琐工作。如果要求达到较高的性能和较小的开销,内存映射文件是举手可得的最佳机制。
数据共享方法是通过让两个或多个进程映射同一个文件映射对象的视图来实现的,这意味着它们将共享物理存储器的同一个页面。因此,当一个进程将数据写入一个共享文件映射对象的视图时,其他进程可以立即看到它们视图中的数据变更情况。注意,如果多个进程共享单个文件映射对象,那么所有进程必须使用相同的名字来表示该文件映射对象。
让我们观察一个例子,启动一个应用程序。当一个应用程序启动时,系统调用C r e a t e F i l e函数,打开磁盘上的. e x e文件。然后系统调用C r e a t e F i l e M a p p i n g函数,创建一个文件映射对象。最后,系统代表新创建的进程调用M a p Vi e w O f F i l e E x函数(它带有S E C _ I M A G E标志),这样, . e x e文件就可以映射到进程的地址空间。这里调用的是M a p Vi e w O f F i l e E x,而不是M a p Vi e w O f F i l e,这样,文件的映像将被映射到存放在. e x e文件映像中的基地址中。系统创建该进程的主线程,将该映射视图的可执行代码的第一个字节的地址放入线程的指令指针,然后C P U启动该代码的运行。
如果用户运行同一个应用程序的第二个实例,系统就认为规定的. e x e文件已经存在一个文件映射对象,因此不会创建新的文件对象或者文件映射对象。相反,系统将第二次映射该文件的一个视图,这次是在新创建的进程的地址空间环境中映射的。系统所做的工作是将相同的文件同时映射到两个地址空间。显然,这是对内存的更有效的使用,因为两个进程将共享包含正在执行的这部分代码的物理存储器的同一个页面。
与所有内核对象一样,可以使用3种方法与多个进程共享对象,这3种方法是句柄继承性、句柄命名和句柄复制。
内存映射文件与数据视图的相关性
附录:
系统允许你映射一个文件的相同数据的多个视图。例如,你可以将文件开头的10 KB映射到一个视图,然后将同一个文件的头4 KB映射到另一个视图。只要你是映射相同的文件映射对象,系统就会确保映射的视图数据的相关性。例如,如果你的应用程序改变了一个视图中的文件内容,那么所有其他视图均被更新以反映这个变化。这是因为尽管页面多次被映射到进程的虚拟地址空间,但是系统只将数据放在单个R A M页面上。如果多个进程映射单个数据文件的视图,那么数据仍然是相关的,因为在数据文件中,每个R A M页面只有一个实例——正是这个R A M页面被映射到多个进程的地址空间。
注意Wi n d o w s允许创建若干个由单个数据文件支持的文件映射对象。Wi n d o w s不能保证这些不同的文件映射对象的视图具有相关性。它只能保证单个文件映射对象的多个视图具有相关性。
然而,当对文件进行操作时,没有理由使另一个应用程序无法调用C r e a t e F i l e函数以打开由另一个进程映射的同一个文件。这个新进程可以使用R e a d F i l e和Wr i t e F i l e函数来读取该文件的数据和将数据写入该文件。当然,每当一个进程调用这些函数时,它必须从内存缓冲区读取文件数据或者将文件数据写入内存缓冲区。该内存缓冲区必须是进程自己创建的一个缓冲区,而不是映射文件使用的内存缓冲区。当两个应用程序打开同一个文件时,问题就可能产生:一个进程可以调用R e a d F i l e函数来读取文件的一个部分,并修改它的数据,然后使用Wr i t e F i l e函数将数据重新写入文件,而第二个进程的文件映射对象却不知道第一个进程执行的这些操作。由于这个原因,当你为将被内存映射的文件调用C r e a t e F i l e函数时,最好将d w S h a r e M o d e参数的值设置为0。这样就可以告诉系统,你想要单独访问这个文件,而其他进程都不能打开它。
只读文件不存在相关性问题,因此它们可以作为很好的内存映射文件。内存映射文件决不应该用于共享网络上的可写入文件,因为系统无法保证数据视图的相关性。如果某个人的计算机更新了文件的内容,其他内存中含有原始数据的计算机将不知道它的信息已经被修改。
posted @
2006-10-11 13:46 Jerry Cat 阅读(751) |
评论 (0) |
编辑 收藏
[转]Visual C++线程同步技术
线程同步的方式有:
临界区
管理事件内核对象
信号量内核对象
互斥内核对象
分别介绍如下:
使线程同步
在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作。更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解。正常情况下对这种处理结果的了解应当在其处理任务完成后进行。
如果不采取适当的措施,其他线程往往会在线程处理任务结束前就去访问处理结果,这就很有可能得到有关处理结果的错误了解。例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。
为了确保读线程读取到的是经过修改的变量,就必须在向变量写入数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。象这种保证线程能了解其他线程任务处理结束后的处理结果而采取的保护措施即为线程同步。
线程同步是一个非常大的话题,包括方方面面的内容。从大的方面讲,线程的同步可分用户模式的线程同步和内核对象的线程同步两大类。用户模式中线程的同步方法主要有原子访问和临界区等方法。其特点是同步速度特别快,适合于对线程运行速度有严格要求的场合。
内核对象的线程同步则主要由事件、等待定时器、信号量以及信号灯等内核对象构成。由于这种同步机制使用了内核对象,使用时必须将线程从用户模式切换到内核模式,而这种转换一般要耗费近千个CPU周期,因此同步速度较慢,但在适用性上却要远优于用户模式的线程同步方式。
临界区
临界区(Critical Section)是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。
临界区在使用时以CRITICAL_SECTION结构对象保护共享资源,并分别用EnterCriticalSection()和LeaveCriticalSection()函数去标识和释放一个临界区。所用到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有被破坏的可能。
图1 使用临界区保持线程同步
下面通过一段代码展示了临界区在保护多线程访问的共享资源中的作用。通过两个线程来分别对全局变量g_cArray[10]进行写入操作,用临界区结构对象g_cs来保持线程的同步,并在开启线程前对其进行初始化。为了使实验效果更加明显,体现出临界区的作用,在线程函数对共享资源g_cArray[10]的写入时,以Sleep()函数延迟1毫秒,使其他线程同其抢占CPU的可能性增大。如果不使用临界区对其进行保护,则共享资源数据将被破坏(参见图1(a)所示计算结果),而使用临界区对线程保持同步后则可以得到正确的结果(参见图1(b)所示计算结果)。代码实现清单附下:
// 临界区结构对象 CRITICAL_SECTION g_cs; // 共享资源 char g_cArray[10]; UINT ThreadProc10(LPVOID pParam) { // 进入临界区 EnterCriticalSection(&g_cs); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 离开临界区 LeaveCriticalSection(&g_cs); return 0; } UINT ThreadProc11(LPVOID pParam) { // 进入临界区 EnterCriticalSection(&g_cs); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // 离开临界区 LeaveCriticalSection(&g_cs); return 0; } …… void CSample08View::OnCriticalSection() { // 初始化临界区 InitializeCriticalSection(&g_cs); // 启动线程 AfxBeginThread(ThreadProc10, NULL); AfxBeginThread(ThreadProc11, NULL); // 等待计算完毕 Sleep(300); // 报告计算结果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
|
在使用临界区时,一般不允许其运行时间过长,只要进入临界区的线程还没有离开,其他所有试图进入此临界区的线程都会被挂起而进入到等待状态,并会在一定程度上影响。程序的运行性能。尤其需要注意的是不要将等待用户输入或是其他一些外界干预的操作包含到临界区。如果进入了临界区却一直没有释放,同样也会引起其他线程的长时间等待。换句话说,在执行了EnterCriticalSection()语句进入临界区后无论发生什么,必须确保与之匹配的LeaveCriticalSection()都能够被执行到。可以通过添加结构化异常处理代码来确保LeaveCriticalSection()语句的执行。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。
MFC为临界区提供有一个CCriticalSection类,使用该类进行线程同步处理是非常简单的,只需在线程函数中用CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片段即可。对于上述代码,可通过CCriticalSection类将其改写如下:
// MFC临界区类对象 CCriticalSection g_clsCriticalSection; // 共享资源 char g_cArray[10]; UINT ThreadProc20(LPVOID pParam) { // 进入临界区 g_clsCriticalSection.Lock(); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 离开临界区 g_clsCriticalSection.Unlock(); return 0; } UINT ThreadProc21(LPVOID pParam) { // 进入临界区 g_clsCriticalSection.Lock(); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // 离开临界区 g_clsCriticalSection.Unlock(); return 0; } …… void CSample08View::OnCriticalSectionMfc() { // 启动线程 AfxBeginThread(ThreadProc20, NULL); AfxBeginThread(ThreadProc21, NULL); // 等待计算完毕 Sleep(300); // 报告计算结果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
|
管理事件内核对象
在前面讲述线程通信时曾使用过事件内核对象来进行线程间的通信,除此之外,事件内核对象也可以通过通知操作的方式来保持线程的同步。对于前面那段使用临界区保持线程同步的代码可用事件对象的线程同步方法改写如下:
// 事件句柄 HANDLE hEvent = NULL; // 共享资源 char g_cArray[10]; …… UINT ThreadProc12(LPVOID pParam) { // 等待事件置位 WaitForSingleObject(hEvent, INFINITE); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 处理完成后即将事件对象置位 SetEvent(hEvent); return 0; } UINT ThreadProc13(LPVOID pParam) { // 等待事件置位 WaitForSingleObject(hEvent, INFINITE); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // 处理完成后即将事件对象置位 SetEvent(hEvent); return 0; } …… void CSample08View::OnEvent() { // 创建事件 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // 事件置位 SetEvent(hEvent); // 启动线程 AfxBeginThread(ThreadProc12, NULL); AfxBeginThread(ThreadProc13, NULL); // 等待计算完毕 Sleep(300); // 报告计算结果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
|
在创建线程前,首先创建一个可以自动复位的事件内核对象hEvent,而线程函数则通过WaitForSingleObject()等待函数无限等待hEvent的置位,只有在事件置位时WaitForSingleObject()才会返回,被保护的代码将得以执行。对于以自动复位方式创建的事件对象,在其置位后一被WaitForSingleObject()等待到就会立即复位,也就是说在执行ThreadProc12()中的受保护代码时,事件对象已经是复位状态的,这时即使有ThreadProc13()对CPU的抢占,也会由于WaitForSingleObject()没有hEvent的置位而不能继续执行,也就没有可能破坏受保护的共享资源。在ThreadProc12()中的处理完成后可以通过SetEvent()对hEvent的置位而允许ThreadProc13()对共享资源g_cArray的处理。这里SetEvent()所起的作用可以看作是对某项特定任务完成的通知。
使用临界区只能同步同一进程中的线程,而使用事件内核对象则可以对进程外的线程进行同步,其前提是得到对此事件对象的访问权。可以通过OpenEvent()函数获取得到,其函数原型为:
HANDLE OpenEvent( DWORD dwDesiredAccess, // 访问标志 BOOL bInheritHandle, // 继承标志 LPCTSTR lpName // 指向事件对象名的指针 );
|
如果事件对象已创建(在创建事件时需要指定事件名),函数将返回指定事件的句柄。对于那些在创建事件时没有指定事件名的事件内核对象,可以通过使用内核对象的继承性或是调用DuplicateHandle()函数来调用CreateEvent()以获得对指定事件对象的访问权。在获取到访问权后所进行的同步操作与在同一个进程中所进行的线程同步操作是一样的。
如果需要在一个线程中等待多个事件,则用WaitForMultipleObjects()来等待。WaitForMultipleObjects()与WaitForSingleObject()类似,同时监视位于句柄数组中的所有句柄。这些被监视对象的句柄享有平等的优先权,任何一个句柄都不可能比其他句柄具有更高的优先权。WaitForMultipleObjects()的函数原型为:
DWORD WaitForMultipleObjects( DWORD nCount, // 等待句柄数 CONST HANDLE *lpHandles, // 句柄数组首地址 BOOL fWaitAll, // 等待标志 DWORD dwMilliseconds // 等待时间间隔 );
|
参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。dwMilliseconds在这里的作用与在WaitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1中的某个值,则说明所有指定对象的状态均为已通知状态(当fWaitAll为TRUE时)或是用以减去WAIT_OBJECT_0而得到发生通知的对象的索引(当fWaitAll为FALSE时)。如果返回值在WAIT_ABANDONED_0与WAIT_ABANDONED_0+nCount-1之间,则表示所有指定对象的状态均为已通知,且其中至少有一个对象是被丢弃的互斥对象(当fWaitAll为TRUE时),或是用以减去WAIT_OBJECT_0表示一个等待正常结束的互斥对象的索引(当fWaitAll为FALSE时)。 下面给出的代码主要展示了对WaitForMultipleObjects()函数的使用。通过对两个事件内核对象的等待来控制线程任务的执行与中途退出:
// 存放事件句柄的数组 HANDLE hEvents[2]; UINT ThreadProc14(LPVOID pParam) { // 等待开启事件 DWORD dwRet1 = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE); // 如果开启事件到达则线程开始执行任务 if (dwRet1 == WAIT_OBJECT_0) { AfxMessageBox("线程开始工作!"); while (true) { for (int i = 0; i < 10000; i++); // 在任务处理过程中等待结束事件 DWORD dwRet2 = WaitForMultipleObjects(2, hEvents, FALSE, 0); // 如果结束事件置位则立即终止任务的执行 if (dwRet2 == WAIT_OBJECT_0 + 1) break; } } AfxMessageBox("线程退出!"); return 0; } …… void CSample08View::OnStartEvent() { // 创建线程 for (int i = 0; i < 2; i++) hEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL); // 开启线程 AfxBeginThread(ThreadProc14, NULL); // 设置事件0(开启事件) SetEvent(hEvents[0]); } void CSample08View::OnEndevent() { // 设置事件1(结束事件) SetEvent(hEvents[1]); }
|
MFC为事件相关处理也提供了一个CEvent类,共包含有除构造函数外的4个成员函数PulseEvent()、ResetEvent()、SetEvent()和UnLock()。在功能上分别相当与Win32 API的PulseEvent()、ResetEvent()、SetEvent()和CloseHandle()等函数。而构造函数则履行了原CreateEvent()函数创建事件对象的职责,其函数原型为:
CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
|
按照此缺省设置将创建一个自动复位、初始状态为复位状态的没有名字的事件对象。封装后的CEvent类使用起来更加方便,图2即展示了CEvent类对A、B两线程的同步过程:
图2 CEvent类对线程的同步过程示意
B线程在执行到CEvent类成员函数Lock()时将会发生阻塞,而A线程此时则可以在没有B线程干扰的情况下对共享资源进行处理,并在处理完成后通过成员函数SetEvent()向B发出事件,使其被释放,得以对A先前已处理完毕的共享资源进行操作。可见,使用CEvent类对线程的同步方法与通过API函数进行线程同步的处理方法是基本一致的。前面的API处理代码可用CEvent类将其改写为:
// MFC事件类对象 CEvent g_clsEvent; UINT ThreadProc22(LPVOID pParam) { // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 事件置位 g_clsEvent.SetEvent(); return 0; } UINT ThreadProc23(LPVOID pParam) { // 等待事件 g_clsEvent.Lock(); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } return 0; } …… void CSample08View::OnEventMfc() { // 启动线程 AfxBeginThread(ThreadProc22, NULL); AfxBeginThread(ThreadProc23, NULL); // 等待计算完毕 Sleep(300); // 报告计算结果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
|
信号量内核对象
信号量(Semaphore)内核对象对线程的同步方式与前面几种方法不同,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。
图3 使用信号量对象控制资源
下面结合图例3来演示信号量对象对资源的控制。在图3中,以箭头和白色箭头表示共享资源所允许的最大资源计数和当前可用资源计数。初始如图(a)所示,最大资源计数和当前可用资源计数均为4,此后每增加一个对资源进行访问的线程(用黑色箭头表示)当前资源计数就会相应减1,图(b)即表示的在3个线程对共享资源进行访问时的状态。当进入线程数达到4个时,将如图(c)所示,此时已达到最大资源计数,而当前可用资源计数也已减到0,其他线程无法对共享资源进行访问。在当前占有资源的线程处理完毕而退出后,将会释放出空间,图(d)已有两个线程退出对资源的占有,当前可用计数为2,可以再允许2个线程进入到对资源的处理。可以看出,信号量是通过计数来对线程访问资源进行控制的,而实际上信号量确实也被称作Dijkstra计数器。
使用信号量内核对象进行线程同步主要会用到CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects()等函数。其中,CreateSemaphore()用来创建一个信号量内核对象,其函数原型为:
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性指针 LONG lInitialCount, // 初始计数 LONG lMaximumCount, // 最大计数 LPCTSTR lpName // 对象名指针 );
|
参数lMaximumCount是一个有符号32位值,定义了允许的最大资源计数,最大取值不能超过4294967295。lpName参数可以为创建的信号量定义一个名字,由于其创建的是一个内核对象,因此在其他进程中可以通过该名字而得到此信号量。OpenSemaphore()函数即可用来根据信号量名打开在其他进程中创建的信号量,函数原型如下:
HANDLE OpenSemaphore( DWORD dwDesiredAccess, // 访问标志 BOOL bInheritHandle, // 继承标志 LPCTSTR lpName // 信号量名 );
|
在线程离开对共享资源的处理时,必须通过ReleaseSemaphore()来增加当前可用资源计数。否则将会出现当前正在处理共享资源的实际线程数并没有达到要限制的数值,而其他线程却因为当前可用资源计数为0而仍无法进入的情况。ReleaseSemaphore()的函数原型为:
BOOL ReleaseSemaphore( HANDLE hSemaphore, // 信号量句柄 LONG lReleaseCount, // 计数递增数量 LPLONG lpPreviousCount // 先前计数 );
|
该函数将lReleaseCount中的值添加给信号量的当前资源计数,一般将lReleaseCount设置为1,如果需要也可以设置其他的值。WaitForSingleObject()和WaitForMultipleObjects()主要用在试图进入共享资源的线程函数入口处,主要用来判断信号量的当前可用资源计数是否允许本线程的进入。只有在当前可用资源计数值大于0时,被监视的信号量内核对象才会得到通知。
信号量的使用特点使其更适用于对Socket(套接字)程序中线程的同步。例如,网络上的HTTP服务器要对同一时间内访问同一页面的用户数加以限制,这时可以为没一个用户对服务器的页面请求设置一个线程,而页面则是待保护的共享资源,通过使用信号量对线程的同步作用可以确保在任一时刻无论有多少用户对某一页面进行访问,只有不大于设定的最大用户数目的线程能够进行访问,而其他的访问企图则被挂起,只有在有用户退出对此页面的访问后才有可能进入。下面给出的示例代码即展示了类似的处理过程:
// 信号量对象句柄 HANDLE hSemaphore; UINT ThreadProc15(LPVOID pParam) { // 试图进入信号量关口 WaitForSingleObject(hSemaphore, INFINITE); // 线程任务处理 AfxMessageBox("线程一正在执行!"); // 释放信号量计数 ReleaseSemaphore(hSemaphore, 1, NULL); return 0; } UINT ThreadProc16(LPVOID pParam) { // 试图进入信号量关口 WaitForSingleObject(hSemaphore, INFINITE); // 线程任务处理 AfxMessageBox("线程二正在执行!"); // 释放信号量计数 ReleaseSemaphore(hSemaphore, 1, NULL); return 0; } UINT ThreadProc17(LPVOID pParam) { // 试图进入信号量关口 WaitForSingleObject(hSemaphore, INFINITE); // 线程任务处理 AfxMessageBox("线程三正在执行!"); // 释放信号量计数 ReleaseSemaphore(hSemaphore, 1, NULL); return 0; } …… void CSample08View::OnSemaphore() { // 创建信号量对象 hSemaphore = CreateSemaphore(NULL, 2, 2, NULL); // 开启线程 AfxBeginThread(ThreadProc15, NULL); AfxBeginThread(ThreadProc16, NULL); AfxBeginThread(ThreadProc17, NULL); }
|
图4 开始进入的两个线程
图5 线程二退出后线程三才得以进入
上述代码在开启线程前首先创建了一个初始计数和最大资源计数均为2的信号量对象hSemaphore。即在同一时刻只允许2个线程进入由hSemaphore保护的共享资源。随后开启的三个线程均试图访问此共享资源,在前两个线程试图访问共享资源时,由于hSemaphore的当前可用资源计数分别为2和1,此时的hSemaphore是可以得到通知的,也就是说位于线程入口处的WaitForSingleObject()将立即返回,而在前两个线程进入到保护区域后,hSemaphore的当前资源计数减少到0,hSemaphore将不再得到通知,WaitForSingleObject()将线程挂起。直到此前进入到保护区的线程退出后才能得以进入。图4和图5为上述代脉的运行结果。从实验结果可以看出,信号量始终保持了同一时刻不超过2个线程的进入。
在MFC中,通过CSemaphore类对信号量作了表述。该类只具有一个构造函数,可以构造一个信号量对象,并对初始资源计数、最大资源计数、对象名和安全属性等进行初始化,其原型如下:
CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );
|
在构造了CSemaphore类对象后,任何一个访问受保护共享资源的线程都必须通过CSemaphore从父类CSyncObject类继承得到的Lock()和UnLock()成员函数来访问或释放CSemaphore对象。与前面介绍的几种通过MFC类保持线程同步的方法类似,通过CSemaphore类也可以将前面的线程同步代码进行改写,这两种使用信号量的线程同步方法无论是在实现原理上还是从实现结果上都是完全一致的。下面给出经MFC改写后的信号量线程同步代码:
// MFC信号量类对象 CSemaphore g_clsSemaphore(2, 2); UINT ThreadProc24(LPVOID pParam) { // 试图进入信号量关口 g_clsSemaphore.Lock(); // 线程任务处理 AfxMessageBox("线程一正在执行!"); // 释放信号量计数 g_clsSemaphore.Unlock(); return 0; } UINT ThreadProc25(LPVOID pParam) { // 试图进入信号量关口 g_clsSemaphore.Lock(); // 线程任务处理 AfxMessageBox("线程二正在执行!"); // 释放信号量计数 g_clsSemaphore.Unlock(); return 0; } UINT ThreadProc26(LPVOID pParam) { // 试图进入信号量关口 g_clsSemaphore.Lock(); // 线程任务处理 AfxMessageBox("线程三正在执行!"); // 释放信号量计数 g_clsSemaphore.Unlock(); return 0; } …… void CSample08View::OnSemaphoreMfc() { // 开启线程 AfxBeginThread(ThreadProc24, NULL); AfxBeginThread(ThreadProc25, NULL); AfxBeginThread(ThreadProc26, NULL); }
|
互斥内核对象
互斥(Mutex)是一种用途非常广泛的内核对象。能够保证多个线程对同一共享资源的互斥访问。同临界区有些类似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。与其他几种内核对象不同,互斥对象在操作系统中拥有特殊代码,并由操作系统来管理,操作系统甚至还允许其进行一些其他内核对象所不能进行的非常规操作。为便于理解,可参照图6给出的互斥内核对象的工作模型:
图6 使用互斥内核对象对共享资源的保护
图(a)中的箭头为要访问资源(矩形框)的线程,但只有第二个线程拥有互斥对象(黑点)并得以进入到共享资源,而其他线程则会被排斥在外(如图(b)所示)。当此线程处理完共享资源并准备离开此区域时将把其所拥有的互斥对象交出(如图(c)所示),其他任何一个试图访问此资源的线程都有机会得到此互斥对象。
以互斥内核对象来保持线程同步可能用到的函数主要有CreateMutex()、OpenMutex()、ReleaseMutex()、WaitForSingleObject()和WaitForMultipleObjects()等。在使用互斥对象前,首先要通过CreateMutex()或OpenMutex()创建或打开一个互斥对象。CreateMutex()函数原型为:
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全属性指针 BOOL bInitialOwner, // 初始拥有者 LPCTSTR lpName // 互斥对象名 );
|
参数bInitialOwner主要用来控制互斥对象的初始状态。一般多将其设置为FALSE,以表明互斥对象在创建时并没有为任何线程所占有。如果在创建互斥对象时指定了对象名,那么可以在本进程其他地方或是在其他进程通过OpenMutex()函数得到此互斥对象的句柄。OpenMutex()函数原型为:
HANDLE OpenMutex( DWORD dwDesiredAccess, // 访问标志 BOOL bInheritHandle, // 继承标志 LPCTSTR lpName // 互斥对象名 );
|
当目前对资源具有访问权的线程不再需要访问此资源而要离开时,必须通过ReleaseMutex()函数来释放其拥有的互斥对象,其函数原型为:
BOOL ReleaseMutex(HANDLE hMutex);
|
其唯一的参数hMutex为待释放的互斥对象句柄。至于WaitForSingleObject()和WaitForMultipleObjects()等待函数在互斥对象保持线程同步中所起的作用与在其他内核对象中的作用是基本一致的,也是等待互斥内核对象的通知。但是这里需要特别指出的是:在互斥对象通知引起调用等待函数返回时,等待函数的返回值不再是通常的WAIT_OBJECT_0(对于WaitForSingleObject()函数)或是在WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1之间的一个值(对于WaitForMultipleObjects()函数),而是将返回一个WAIT_ABANDONED_0(对于WaitForSingleObject()函数)或是在WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1之间的一个值(对于WaitForMultipleObjects()函数)。以此来表明线程正在等待的互斥对象由另外一个线程所拥有,而此线程却在使用完共享资源前就已经终止。除此之外,使用互斥对象的方法在等待线程的可调度性上同使用其他几种内核对象的方法也有所不同,其他内核对象在没有得到通知时,受调用等待函数的作用,线程将会挂起,同时失去可调度性,而使用互斥的方法却可以在等待的同时仍具有可调度性,这也正是互斥对象所能完成的非常规操作之一。
在编写程序时,互斥对象多用在对那些为多个线程所访问的内存块的保护上,可以确保任何线程在处理此内存块时都对其拥有可靠的独占访问权。下面给出的示例代码即通过互斥内核对象hMutex对共享内存快g_cArray[]进行线程的独占访问保护。下面给出实现代码清单:
// 互斥对象 HANDLE hMutex = NULL; char g_cArray[10]; UINT ThreadProc18(LPVOID pParam) { // 等待互斥对象通知 WaitForSingleObject(hMutex, INFINITE); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 释放互斥对象 ReleaseMutex(hMutex); return 0; } UINT ThreadProc19(LPVOID pParam) { // 等待互斥对象通知 WaitForSingleObject(hMutex, INFINITE); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // 释放互斥对象 ReleaseMutex(hMutex); return 0; } …… void CSample08View::OnMutex() { // 创建互斥对象 hMutex = CreateMutex(NULL, FALSE, NULL); // 启动线程 AfxBeginThread(ThreadProc18, NULL); AfxBeginThread(ThreadProc19, NULL); // 等待计算完毕 Sleep(300); // 报告计算结果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
|
互斥对象在MFC中通过CMutex类进行表述。使用CMutex类的方法非常简单,在构造CMutex类对象的同时可以指明待查询的互斥对象的名字,在构造函数返回后即可访问此互斥变量。CMutex类也是只含有构造函数这唯一的成员函数,当完成对互斥对象保护资源的访问后,可通过调用从父类CSyncObject继承的UnLock()函数完成对互斥对象的释放。CMutex类构造函数原型为:
CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
|
该类的适用范围和实现原理与API方式创建的互斥内核对象是完全类似的,但要简洁的多,下面给出就是对前面的示例代码经CMutex类改写后的程序实现清单:
// MFC互斥类对象 CMutex g_clsMutex(FALSE, NULL); UINT ThreadProc27(LPVOID pParam) { // 等待互斥对象通知 g_clsMutex.Lock(); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[i] = 'a'; Sleep(1); } // 释放互斥对象 g_clsMutex.Unlock(); return 0; } UINT ThreadProc28(LPVOID pParam) { // 等待互斥对象通知 g_clsMutex.Lock(); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = 'b'; Sleep(1); } // 释放互斥对象 g_clsMutex.Unlock(); return 0; } …… void CSample08View::OnMutexMfc() { // 启动线程 AfxBeginThread(ThreadProc27, NULL); AfxBeginThread(ThreadProc28, NULL); // 等待计算完毕 Sleep(300); // 报告计算结果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }
|
小结
线程的使用使程序处理更够更加灵活,而这种灵活同样也会带来各种不确定性的可能。尤其是在多个线程对同一公共变量进行访问时。虽然未使用线程同步的程序代码在逻辑上或许没有什么问题,但为了确保程序的正确、可靠运行,必须在适当的场合采取线程同步措施。
posted @
2006-10-10 17:52 Jerry Cat 阅读(210) |
评论 (0) |
编辑 收藏
[转]VC++编程获取机器的MAC地址:
NetBIOS编程篇
#include
<
windows.h
>
#include
<
wincon.h
>
#include
<
stdlib.h
>
#include
<
stdio.h
>
#include
<
time.h
>
typedef
struct
_ASTAT_
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff [
30
];
}ASTAT,
*
PASTAT;
ASTAT Adapter;
void
main (
void
)
{
NCB Ncb;
UCHAR uRetCode;
char
NetName[
50
];
LANA_ENUM lenum;
int
i;
memset(
&
Ncb,
0
,
sizeof
(Ncb) );
Ncb.ncb_command
=
NCBENUM;
Ncb.ncb_buffer
=
(UCHAR
*
)
&
lenum;
Ncb.ncb_length
=
sizeof
(lenum);
uRetCode
=
Netbios(
&
Ncb );
printf(
"
The NCBENUM return code is: 0x%x \n
"
, uRetCode );
for
(i
=
0
; i
<
lenum.length ;i
++
)
{
memset(
&
Ncb,
0
,
sizeof
(Ncb) );
Ncb.ncb_command
=
NCBRESET;
Ncb.ncb_lana_num
=
lenum.lana[i];
uRetCode
=
Netbios(
&
Ncb );
printf(
"
The NCBRESET on LANA %d return code is: 0x%x \n
"
,
lenum.lana[i], uRetCode );
memset(
&
Ncb,
0
,
sizeof
(Ncb) );
Ncb.ncb_command
=
NCBASTAT;
Ncb.ncb_lana_num
=
lenum.lana[i];
strcpy((
char
*
)Ncb.ncb_callname,
"
*
"
);
Ncb.ncb_buffer
=
(unsigned
char
*
)
&
Adapter;
Ncb.ncb_length
=
sizeof
(Adapter);
uRetCode
=
Netbios(
&
Ncb );
printf(
"
The NCBASTAT on LANA %d return code is: 0x%x \n
"
,
lenum.lana[i], uRetCode );
if
( uRetCode
==
0
)
{
printf(
"
The Ethernet Number on LANA %d is:%02x-%02x-%02x-%02x-%02x-%02x\n
"
,lenum.lana[i],
Adapter.adapt.adapter_address[
0
],
Adapter.adapt.adapter_address[
1
],
Adapter.adapt.adapter_address[
2
],
Adapter.adapt.adapter_address[
3
],
Adapter.adapt.adapter_address[
4
],
Adapter.adapt.adapter_address[
5
]);
}
}
system(
"
PAUSE
"
);
}
所用的资料:
NetBIOS API编程,NCBENUM命令调用,ADAPTER_STATUS结构等。
[摘自MSDN]
ADAPTER_STATUS
The ADAPTER_STATUS structure contains information about a network adapter. This structure is pointed to by the ncb_buffer member of the
NCB
structure. ADAPTER_STATUS is followed by as many
NAME_BUFFER
structures as required to describe the network adapters on the system.
typedef
struct
_ADAPTER_STATUS {
UCHAR adapter_address[
6
];
UCHAR rev_major;
UCHAR reserved0;
UCHAR adapter_type;
UCHAR rev_minor;
WORD duration;
WORD frmr_recv;
WORD frmr_xmit;
WORD iframe_recv_err;
WORD xmit_aborts;
DWORD xmit_success;
DWORD recv_success;
WORD iframe_xmit_err;
WORD recv_buff_unavail;
WORD t1_timeouts;
WORD ti_timeouts;
DWORD reserved1;
WORD free_ncbs;
WORD max_cfg_ncbs;
WORD max_ncbs;
WORD xmit_buf_unavail;
WORD max_dgram_size;
WORD pending_sess;
WORD max_cfg_sess;
WORD max_sess;
WORD max_sess_pkt_size;
WORD name_count;
} ADAPTER_STATUS,
*
PADAPTER_STATUS;
Members
-
adapter_address
-
Specifies encoded address of the adapter.
-
rev_major
-
Specifies the major software-release level. This value is 3 for IBM NetBIOS 3. x.
-
reserved0
-
Reserved. This value is always zero.
-
adapter_type
-
Specifies the adapter type. This value is 0xFF for a Token Ring adapter or 0xFE for an Ethernet adapter.
-
rev_minor
-
Specifies the minor software-release level. This value is zero for IBM NetBIOS x.0.
-
duration
-
Specifies the duration of the reporting period, in minutes.
-
frmr_recv
-
Specifies the number of FRMR frames received.
-
frmr_xmit
-
Specifies the number of FRMR frames transmitted.
-
iframe_recv_err
-
Specifies the number of I frames received in error.
-
xmit_aborts
-
Specifies the number of aborted transmissions.
-
xmit_success
-
Specifies the number of successfully transmitted packets.
-
recv_success
-
Specifies the number of successfully received packets.
-
iframe_xmit_err
-
Specifies the number of I frames transmitted in error.
-
recv_buff_unavail
-
Specifies the number of times a buffer was not available to service a request from a remote computer.
-
t1_timeouts
-
Specifies the number of times that the DLC T1 timer timed out.
Windows XP DLC will no longer be supported. For more information, see
Network Protocol Support in Windows
.
-
ti_timeouts
-
Specifies the number of times that the ti inactivity timer timed out. The ti timer is used to detect links that have been broken.
-
reserved1
-
Reserved. This value is always zero.
-
free_ncbs
-
Specifies the current number of free network control blocks.
-
max_cfg_ncbs
-
Undefined for IBM NetBIOS 3.0.
-
max_ncbs
-
Undefined for IBM NetBIOS 3.0.
-
xmit_buf_unavail
-
Undefined for IBM NetBIOS 3.0.
-
max_dgram_size
-
Specifies the maximum size of a datagram packet. This value is always at least 512 bytes.
-
pending_sess
-
Specifies the number of pending sessions.
-
max_cfg_sess
-
Specifies the configured maximum pending sessions.
-
max_sess
-
Undefined for IBM NetBIOS 3.0.
-
max_sess_pkt_size
-
Specifies the maximum size of a session data packet.
-
name_count
-
Specifies the number of names in the local names table.
Requirements
Windows NT/2000/XP: Included in Windows NT 3.1 and later.
Windows 95/98/Me: Included in Windows 95 and later.
Header: Declared in Nb30.h.
posted @
2006-10-10 16:59 Jerry Cat 阅读(602) |
评论 (0) |
编辑 收藏
[转]XP一个很无敌的命令
XP有一个很无敌的命令,用来替换文件的replace,连正在使用的文件也能替换。非常无敌。 比如:在C:下建一个目录,c:aaa ,然后复制一首mp3到c:aaa并命名为c:aaaa.mp3 ,然后再复制另一首歌到C:a.mp3 ,然后用media player 播放c:aaaa.mp3 ,在命令提示符下输入:replace c:a.mp3 c:aaa ,过一会,是不是播放的歌已变为另一首。
用这个命令来替换系统文件真是太爽了,并且XP的系统文件保护也对它无效。 再也不用到安全模式下去替换文件了
格式
REPLACE [drive1:][path1]filename [drive2:][path2] [/A] [/P] [/R] [/W]
REPLACE [drive1:][path1]filename [drive2:][path2] [/P] [/R] [/S] [/W]
[drive1:][path1]filename 指定源文件。
[drive2:][path2] 指定要替换文件的
目录
/A 把新文件加入目标目录。不能和/S 或 /U 命令行开关搭配使用。 /P 替换文件或加入源文件之前会先提示您进行确认。 /R 替换只读文件以及未受保护的文件。 /S 替换目标目录中所有子目录的文件。 不能与 /A 命令选项搭配使用。
/W 等您插入磁盘以后再运行。
/U 只会替换或更新比源文件日期早的文件。 不能与 /A 命令行开关搭配使用。
posted @
2006-10-08 16:37 Jerry Cat 阅读(724) |
评论 (2) |
编辑 收藏
/********************************************\
| 欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉! |
\********************************************/
Vista下编程的困惑 - C#最好还是不要用于桌面应用
作者: Jerry Cat
时间: 2006/09/26
链接: http://www.cppblog.com/jerysun0818/archive/2006/09/26/12966.html
比如说,很多在微软的.Net框架之下运行的软件对象都必须要和“Windows动态链接库(Windows dynamic link libraries)”进行交互,而不是直接运行原生的控制代码,这样就导致了这些软件的运行速度十分缓慢。微软公司曾经讨论过用.Net代码重新打造Windows的系统内核,甚至还专门设立了一个研究项目,想要把.Net的通用语言协议(Common Language Runtime)打造成为Windows系统代码的核心。但是,Windows的硬件访问层阻止了这一转变的发生。Huckaby先生评价到:“是否要做出这样的改变,事实上这是一个巨大的商业决定——而根本不是技术性决定......
posted @
2006-09-26 01:03 Jerry Cat 阅读(3016) |
评论 (9) |
编辑 收藏