<!-- /* Font Definitions */ @font-face {font-family:Wingdings; panose-1:5 0 0 0 0 0 0 0 0 0; mso-font-charset:2; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:0 268435456 0 0 -2147483648 0;} @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:-1610611985 1107304683 0 0 159 0;} @font-face {font-family:Cambria; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:-1610611985 1073741899 0 0 159 0;} @font-face {font-family:Calibri; panose-1:2 15 5 2 2 2 4 3 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-1610611985 1073750139 0 0 159 0;} @font-face {font-family:"/@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h1 {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 1 Char"; mso-style-next:正文; margin-top:24.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:1; font-size:14.0pt; font-family:"Cambria","serif"; mso-font-kerning:0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h1.CxSpFirst {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 1 Char"; mso-style-next:正文; mso-style-type:export-only; margin-top:24.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:1; font-size:14.0pt; font-family:"Cambria","serif"; mso-font-kerning:0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h1.CxSpMiddle {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 1 Char"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:1; font-size:14.0pt; font-family:"Cambria","serif"; mso-font-kerning:0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h1.CxSpLast {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 1 Char"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:1; font-size:14.0pt; font-family:"Cambria","serif"; mso-font-kerning:0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h2 {mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 2 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:2; font-size:13.0pt; font-family:"Cambria","serif"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h3 {mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 3 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; line-height:112%; mso-pagination:widow-orphan; mso-outline-level:3; font-size:11.0pt; font-family:"Cambria","serif"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h4 {mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 4 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:4; font-size:11.0pt; font-family:"Cambria","serif"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} h5 {mso-style-noshow:yes; mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 5 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:5; font-size:11.0pt; font-family:"Cambria","serif"; color:#7F7F7F; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} h6 {mso-style-noshow:yes; mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 6 Char"; mso-style-next:正文; margin:0in; margin-bottom:.0001pt; line-height:112%; mso-pagination:widow-orphan; mso-outline-level:6; font-size:11.0pt; font-family:"Cambria","serif"; color:#7F7F7F; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} p.MsoHeading7, li.MsoHeading7, div.MsoHeading7 {mso-style-noshow:yes; mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 7 Char"; mso-style-next:正文; margin:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:7; font-size:11.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} p.MsoHeading8, li.MsoHeading8, div.MsoHeading8 {mso-style-noshow:yes; mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 8 Char"; mso-style-next:正文; margin:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:8; font-size:10.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoHeading9, li.MsoHeading9, div.MsoHeading9 {mso-style-noshow:yes; mso-style-priority:9; mso-style-qformat:yes; mso-style-link:"标题 9 Char"; mso-style-next:正文; margin:0in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; mso-outline-level:9; font-size:10.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} p.MsoToc1, li.MsoToc1, div.MsoToc1 {mso-style-update:auto; mso-style-priority:39; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoToc2, li.MsoToc2, div.MsoToc2 {mso-style-update:auto; mso-style-priority:39; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:11.0pt; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoToc3, li.MsoToc3, div.MsoToc3 {mso-style-update:auto; mso-style-priority:39; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:22.0pt; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoHeader, li.MsoHeader, div.MsoHeader {mso-style-noshow:yes; mso-style-priority:99; mso-style-link:"页眉 Char"; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; text-align:center; line-height:115%; mso-pagination:widow-orphan; tab-stops:center 207.65pt right 415.3pt; layout-grid-mode:char; border:none; mso-border-bottom-alt:solid windowtext .75pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:9.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoFooter, li.MsoFooter, div.MsoFooter {mso-style-priority:99; mso-style-link:"页脚 Char"; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; tab-stops:center 207.65pt right 415.3pt; layout-grid-mode:char; font-size:9.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoTitle, li.MsoTitle, div.MsoTitle {mso-style-priority:10; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 Char"; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; mso-add-space:auto; mso-pagination:widow-orphan; border:none; mso-border-bottom-alt:solid windowtext .5pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:26.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoTitleCxSpFirst, li.MsoTitleCxSpFirst, div.MsoTitleCxSpFirst {mso-style-priority:10; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 Char"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; mso-pagination:widow-orphan; border:none; mso-border-bottom-alt:solid windowtext .5pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:26.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoTitleCxSpMiddle, li.MsoTitleCxSpMiddle, div.MsoTitleCxSpMiddle {mso-style-priority:10; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 Char"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; mso-pagination:widow-orphan; border:none; mso-border-bottom-alt:solid windowtext .5pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:26.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoTitleCxSpLast, li.MsoTitleCxSpLast, div.MsoTitleCxSpLast {mso-style-priority:10; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"标题 Char"; mso-style-next:正文; mso-style-type:export-only; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; mso-add-space:auto; mso-pagination:widow-orphan; border:none; mso-border-bottom-alt:solid windowtext .5pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:26.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoSubtitle, li.MsoSubtitle, div.MsoSubtitle {mso-style-priority:11; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"副标题 Char"; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:30.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; font-size:12.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; letter-spacing:.65pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} p.MsoDate, li.MsoDate, div.MsoDate {mso-style-noshow:yes; mso-style-priority:99; mso-style-link:"日期 Char"; mso-style-next:正文; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:5.0pt; mso-para-margin-top:0in; mso-para-margin-right:0in; mso-para-margin-bottom:10.0pt; mso-para-margin-left:25.0gd; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} a:link, span.MsoHyperlink {mso-style-priority:99; color:#0066FF; text-decoration:underline; text-underline:single;} a:visited, span.MsoHyperlinkFollowed {mso-style-noshow:yes; mso-style-priority:99; color:purple; mso-themecolor:followedhyperlink; text-decoration:underline; text-underline:single;} em {mso-style-priority:20; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; letter-spacing:.5pt; border:none; font-weight:bold;} pre {mso-style-noshow:yes; mso-style-priority:99; mso-style-link:"HTML 预设格式 Char"; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; font-size:12.0pt; font-family:宋体; mso-bidi-font-family:宋体; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoAcetate, li.MsoAcetate, div.MsoAcetate {mso-style-noshow:yes; mso-style-priority:99; mso-style-link:"批注框文本 Char"; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:0in; line-height:115%; mso-pagination:widow-orphan; font-size:9.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoNoSpacing, li.MsoNoSpacing, div.MsoNoSpacing {mso-style-priority:1; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"无间隔 Char"; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph {mso-style-priority:34; mso-style-unhide:no; mso-style-qformat:yes; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:.5in; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoListParagraphCxSpFirst, li.MsoListParagraphCxSpFirst, div.MsoListParagraphCxSpFirst {mso-style-priority:34; mso-style-unhide:no; mso-style-qformat:yes; mso-style-type:export-only; margin-top:0in; margin-right:0in; margin-bottom:0in; margin-left:.5in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoListParagraphCxSpMiddle, li.MsoListParagraphCxSpMiddle, div.MsoListParagraphCxSpMiddle {mso-style-priority:34; mso-style-unhide:no; mso-style-qformat:yes; mso-style-type:export-only; margin-top:0in; margin-right:0in; margin-bottom:0in; margin-left:.5in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoListParagraphCxSpLast, li.MsoListParagraphCxSpLast, div.MsoListParagraphCxSpLast {mso-style-priority:34; mso-style-unhide:no; mso-style-qformat:yes; mso-style-type:export-only; margin-top:0in; margin-right:0in; margin-bottom:10.0pt; margin-left:.5in; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} p.MsoQuote, li.MsoQuote, div.MsoQuote {mso-style-priority:29; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"引用 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:.25in; margin-bottom:0in; margin-left:.25in; margin-bottom:.0001pt; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} p.MsoIntenseQuote, li.MsoIntenseQuote, div.MsoIntenseQuote {mso-style-priority:30; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"明显引用 Char"; mso-style-next:正文; margin-top:10.0pt; margin-right:.8in; margin-bottom:14.0pt; margin-left:.7in; text-align:justify; text-justify:inter-ideograph; line-height:115%; mso-pagination:widow-orphan; border:none; mso-border-bottom-alt:solid windowtext .5pt; padding:0in; mso-padding-alt:0in 0in 1.0pt 0in; font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold; font-style:italic;} span.MsoSubtleEmphasis {mso-style-priority:19; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; font-style:italic;} span.MsoIntenseEmphasis {mso-style-priority:21; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; font-weight:bold;} span.MsoSubtleReference {mso-style-priority:31; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; font-variant:small-caps;} span.MsoIntenseReference {mso-style-priority:32; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; font-variant:small-caps; letter-spacing:.25pt; text-decoration:underline; text-underline:single;} span.MsoBookTitle {mso-style-priority:33; mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; font-variant:small-caps; letter-spacing:.25pt; font-style:italic;} p.MsoTocHeading, li.MsoTocHeading, div.MsoTocHeading {mso-style-noshow:yes; mso-style-priority:39; mso-style-qformat:yes; mso-style-parent:"标题 1"; mso-style-next:正文; margin-top:24.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:14.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} p.MsoTocHeadingCxSpFirst, li.MsoTocHeadingCxSpFirst, div.MsoTocHeadingCxSpFirst {mso-style-noshow:yes; mso-style-priority:39; mso-style-qformat:yes; mso-style-parent:"标题 1"; mso-style-next:正文; mso-style-type:export-only; margin-top:24.0pt; margin-right:0in; margin-bottom:0in; margin-left:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:14.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} p.MsoTocHeadingCxSpMiddle, li.MsoTocHeadingCxSpMiddle, div.MsoTocHeadingCxSpMiddle {mso-style-noshow:yes; mso-style-priority:39; mso-style-qformat:yes; mso-style-parent:"标题 1"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:14.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} p.MsoTocHeadingCxSpLast, li.MsoTocHeadingCxSpLast, div.MsoTocHeadingCxSpLast {mso-style-noshow:yes; mso-style-priority:39; mso-style-qformat:yes; mso-style-parent:"标题 1"; mso-style-next:正文; mso-style-type:export-only; margin:0in; margin-bottom:.0001pt; mso-add-space:auto; line-height:115%; mso-pagination:widow-orphan; font-size:14.0pt; font-family:"Cambria","serif"; mso-fareast-font-family:宋体; mso-bidi-font-family:"Times New Roman"; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} span.1Char {mso-style-name:"标题 1 Char"; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 1"; mso-ansi-font-size:14.0pt; mso-bidi-font-size:14.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} span.2Char {mso-style-name:"标题 2 Char"; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 2"; mso-ansi-font-size:13.0pt; mso-bidi-font-size:13.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} span.3Char {mso-style-name:"标题 3 Char"; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 3"; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} span.4Char {mso-style-name:"标题 4 Char"; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 4"; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold; font-style:italic;} span.5Char {mso-style-name:"标题 5 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 5"; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; color:#7F7F7F; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold;} span.6Char {mso-style-name:"标题 6 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 6"; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; color:#7F7F7F; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold; font-style:italic;} span.7Char {mso-style-name:"标题 7 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 7"; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} span.8Char {mso-style-name:"标题 8 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 8"; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.9Char {mso-style-name:"标题 9 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"标题 9"; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} span.Char {mso-style-name:"无间隔 Char"; mso-style-priority:1; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:无间隔; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char0 {mso-style-name:"批注框文本 Char"; mso-style-noshow:yes; mso-style-priority:99; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:批注框文本; mso-ansi-font-size:9.0pt; mso-bidi-font-size:9.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char1 {mso-style-name:"日期 Char"; mso-style-noshow:yes; mso-style-priority:99; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:日期; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char2 {mso-style-name:"页眉 Char"; mso-style-noshow:yes; mso-style-priority:99; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:页眉; mso-ansi-font-size:9.0pt; mso-bidi-font-size:9.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char3 {mso-style-name:"页脚 Char"; mso-style-priority:99; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:页脚; mso-ansi-font-size:9.0pt; mso-bidi-font-size:9.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.HTMLChar {mso-style-name:"HTML 预设格式 Char"; mso-style-noshow:yes; mso-style-priority:99; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"HTML 预设格式"; mso-ansi-font-size:12.0pt; mso-bidi-font-size:12.0pt; font-family:宋体; mso-ascii-font-family:宋体; mso-hansi-font-family:宋体; mso-bidi-font-family:宋体; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char4 {mso-style-name:"标题 Char"; mso-style-priority:10; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:标题; mso-ansi-font-size:26.0pt; mso-bidi-font-size:26.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; letter-spacing:.25pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US;} span.Char5 {mso-style-name:"副标题 Char"; mso-style-priority:11; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:副标题; mso-ansi-font-size:12.0pt; mso-bidi-font-size:12.0pt; font-family:"Cambria","serif"; mso-ascii-font-family:Cambria; mso-hansi-font-family:Cambria; letter-spacing:.65pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} span.Char6 {mso-style-name:"引用 Char"; mso-style-priority:29; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:引用; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-style:italic;} span.Char7 {mso-style-name:"明显引用 Char"; mso-style-priority:30; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:明显引用; mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; mso-fareast-language:EN-US; mso-bidi-language:EN-US; font-weight:bold; font-style:italic;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:10.0pt; mso-ansi-font-size:10.0pt; mso-bidi-font-size:10.0pt; mso-ascii-font-family:Calibri; mso-fareast-font-family:宋体; mso-hansi-font-family:Calibri;} @page Section1 {size:595.3pt 841.9pt; margin:1.0in .75in 1.0in .75in; mso-header-margin:42.55pt; mso-footer-margin:49.6pt; mso-paper-source:0; layout-grid:15.6pt;} div.Section1 {page:Section1;} /* List Definitions */ @list l0 {mso-list-id:19477224; mso-list-type:hybrid; mso-list-template-ids:-2016751348 957919660 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l0:level1 {mso-level-number-format:decimal-full-width; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l1 {mso-list-id:35618804; mso-list-type:hybrid; mso-list-template-ids:-292357040 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l1:level1 {mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l2 {mso-list-id:37945843; mso-list-type:hybrid; mso-list-template-ids:-557152656 -1167306530 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l2:level1 {mso-level-number-format:decimal-full-width; mso-level-tab-stop:none; mso-level-number-position:left; margin-left:39.0pt; text-indent:-.25in;} @list l3 {mso-list-id:173346659; mso-list-type:hybrid; mso-list-template-ids:-188204378 -1208171458 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l3:level1 {mso-level-text:%1.; mso-level-tab-stop:none; mso-level-number-position:left; margin-left:39.0pt; text-indent:-.25in;} @list l4 {mso-list-id:504593466; mso-list-type:hybrid; mso-list-template-ids:-1887250706 -315474174 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l4:level1 {mso-level-tab-stop:none; mso-level-number-position:left; margin-left:60.0pt; text-indent:-.25in;} @list l5 {mso-list-id:564534336; mso-list-type:hybrid; mso-list-template-ids:-1575576948 1988901484 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l5:level1 {mso-level-number-format:decimal-full-width; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l6 {mso-list-id:654336836; mso-list-type:hybrid; mso-list-template-ids:-1864493826 -586515714 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l6:level1 {mso-level-number-format:japanese-counting; mso-level-text:%1、; mso-level-tab-stop:21.0pt; mso-level-number-position:left; margin-left:21.0pt; text-indent:-21.0pt; mso-ansi-language:EN-US;} @list l6:level2 {mso-level-number-format:alpha-lower; mso-level-text:"%2/)"; mso-level-tab-stop:42.0pt; mso-level-number-position:left; margin-left:42.0pt; text-indent:-21.0pt;} @list l6:level3 {mso-level-number-format:roman-lower; mso-level-tab-stop:63.0pt; mso-level-number-position:right; margin-left:63.0pt; text-indent:-21.0pt;} @list l7 {mso-list-id:687023684; mso-list-type:hybrid; mso-list-template-ids:733275990 2022061108 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l7:level1 {mso-level-number-format:decimal-full-width; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l8 {mso-list-id:1233127868; mso-list-type:hybrid; mso-list-template-ids:527230024 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l8:level1 {mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l9 {mso-list-id:1412118487; mso-list-type:hybrid; mso-list-template-ids:64003442 -1791034928 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l9:level1 {mso-level-text:%1、; mso-level-tab-stop:11.35pt; mso-level-number-position:left; margin-left:11.35pt; text-indent:-11.35pt; mso-ascii-font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-hansi-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman";} @list l10 {mso-list-id:1485200162; mso-list-type:hybrid; mso-list-template-ids:-686660436 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l10:level1 {mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l11 {mso-list-id:1756776902; mso-list-type:hybrid; mso-list-template-ids:-732680122 -1352092336 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l11:level1 {mso-level-text:"/[%1/]"; mso-level-tab-stop:none; mso-level-number-position:left; margin-left:42.0pt; text-indent:-21.0pt;} @list l12 {mso-list-id:1763601758; mso-list-type:hybrid; mso-list-template-ids:-914301096 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l12:level1 {mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in;} @list l13 {mso-list-id:1910336498; mso-list-type:hybrid; mso-list-template-ids:-1182349410 -1791034928 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l13:level1 {mso-level-text:%1、; mso-level-tab-stop:11.35pt; mso-level-number-position:left; margin-left:11.35pt; text-indent:-11.35pt; mso-ascii-font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-hansi-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman";} @list l14 {mso-list-id:2031452156; mso-list-type:hybrid; mso-list-template-ids:1846201960 -136021808 1394390288 -686804554 1760874092 -1771302544 1952606742 1350839770 1571857246 -398039114;} @list l14:level1 {mso-level-number-format:bullet; mso-level-text:; mso-level-tab-stop:.5in; mso-level-number-position:left; text-indent:-.25in; font-family:Wingdings;} @list l15 {mso-list-id:2035185183; mso-list-type:hybrid; mso-list-template-ids:-1612952014 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l15:level1 {mso-level-tab-stop:none; mso-level-number-position:left; margin-left:101.95pt; text-indent:-21.0pt;} ol {margin-bottom:0in;} ul {margin-bottom:0in;} -->
任务1 命令解释器的实现
实践目标:
学会利用Windows API创建进程
实践内容:
将batch文件中所列的应用程序启动运行(批处理)
设计思路:
只要从文件中读出进程名,调用CreateProcess创建进程就可以了。
主要代码结构
- 从程序参数中得到存放进程名的文件名
- 打开文件
- 读出每个进程的名称,用CreateProcess调用
- 用CloseHandle关句柄,减少计数
主要代码分析:
//1.打开文件,这样做其实并不是很好,详见体会
#ifdef UNICODE//make it work whether UNICODE defined.
wifstream inFile(argv[1]);
#else
ifstream inFile(argv[1]);
#endif
int i=0;
//2.得到进程名
while (inFile>>PTSTRCmdLine){
++i;
//3.创建进程
if(!CreateProcess(NULL, PTSTRCmdLine,NULL, NULL, FALSE, CREATE_NEW_CONSOLE|NORMAL_PRIORITY_CLASS, NULL, NULL, &StartInfo,&ProcessInfo) ) {
fprintf(stderr,"Create Process failed (%d)/n",GetLastError());
}else{
fprintf(stdout,"The Child Process: %s's PID: %d./n", PTSTRCmdLine,ProcessInfo.dwProcessId);
}
//4.创建完成后及时关闭句柄(并不结束程序)
CloseHandle(ProcessInfo.hProcess);//close handle to descrease the count, we call it explicitly for good reasons.
CloseHandle(ProcessInfo.hThread);
};
fprintf(stdout,"The Parent Process have to terminate./n");
return (0);
}
实践结果:
基本数据:
源程序代码行数:52
完成实践投入的时间(小时数):0.5
资料查阅时间:0.1
编程调试时间:0.4
测试数据设计:
输入:
calc
notepad
mspaint
it_seems_there_is_no_such_process
输出:
命令行如下,同时启动了计算器等三个程序。
测试结果分析:
前3正常启动;最后一个无此进程,创建失败。
实践体会:
实践过程中遇到的问题及解决过程
关于字符编码:batch文件如果说明是宽字节或单字节(内容、文件名)就容易了,我的处理方法并不是十分正确的(如要考虑所有情况将十分复杂,这里也只是表达一下想法而已)。
实践体会和收获
应该尽量写通用的代码,注重国际化,提供使用Unicode.
参考文献
[1] Jeffrey Richter著,王建华等译.Windows核心编程(第四版)[M].北京:机械工业出版社出版社. 2000
任务2:用软件方法解决兄弟问题
实践目标:
学会使用软件方法解决临界区问题
实践内容:
用Peterson算法或Dekker 算法解决兄弟问题
实践主要内容:
设计思路:
兄弟进程都要从一个账号取钱存到另一账号,这段的实现代码是临界区。
Peterson算法或Dekker 算法都可以软件的方法实现临界区的保护。
正常情况下两账号金额之和为定值。
主要数据结构:
//线程参数
typedef struct BrotherInfo {
int iSerial; //用户定义的线程号
DWORD dwDelay; //线程睡眠的时间
} BROTHERINFO,*PBROTHERINFO;
//目前哪个线程被允许进入临界区
int turn = 0;
//线程提出进入临界区请求的标志位
BOOL flag[MAX_THREADS] ;
主要代码结构 :
- 主线程启动多个(两个)“兄弟”线程,并等待他们全部结束
- 兄弟线程中的关键部分是从一账号取款,存入另一账号,用Dekker 算法保护
主要代码分析:
volatile int iAccnt1 = 0; /* Ensure variable in the memory */
volatile int iAccnt2 = 0;
volatile BOOL flag[2];
volatile int turn;
/*Banker函数负责启动多个Brother线程
*并等待它们结束
*/
void Banker( LPCTSTR file){
int iThreadCounter = 0; // Thread Counter
DWORD dwThreadIdArray[MAX_THREADS]; // Thread ID Array
HANDLE hThreadArray[MAX_THREADS]; // Thread Object HANDLE Array
PBROTHERINFO pThreadInfoArray[MAX_THREADS]; // Custom data structure for threads to use.
flag[0] = flag[1] = false;
turn = 0;
ifstream inFile;
inFile.open(file); //open file
fprintf(stdout, "Now, We begin to Read thread Information and create every WorkerThread /n/n" );
while ( inFile ){
……
//得到线程的相关信息后,一一创建之
// Create the thread to begin execution on its own.
hThreadArray[iThreadCounter] = CreateThread(
NULL, // default security attributes
0, // use default stack size
(LPTHREAD_START_ROUTINE)(Brother), // thread function name
pThreadInfoArray[iThreadCounter], // argument to thread function
0, // use default creation flags
&dwThreadIdArray[iThreadCounter]); // returns the thread identifier
……//容错处理
}
WaitForMultipleObjects(iThreadCounter, hThreadArray, TRUE, INFINITE);
……//清理工作
}
/*Brother函数是重点,主要处理临界区
*/
DWORD WINAPI Brother(LPVOID lpParam){
int iLoan, iAccnt,iCounter = 0;
……
do {
……
int i = pThreadInfo->iSerial-1;
int j = 1-i;
//Dekker 算法,只允许一个线程进入下面的临界区
flag [i] = TRUE;
while (flag[j]){
if (turn == j){
flag[i] = FALSE;
while(turn==j);
flag[i] = TRUE;
}
}
//临界区部分开始
iAccnt1 = iAccnt1 - iLoan;
Sleep(pThreadInfo-> dwDelay*INTE_PER_SEC);
iAccnt2 = iAccnt2 + iLoan;
iAccnt = iAccnt1 + iAccnt2;
//临界区部分结束
turn = j; //此线程已退出临界区
flag[i] = FALSE;
iCounter++;
} while ( (iAccnt == 0) && (iCounter<10));
……
} //End of Brother
基本数据:
源程序代码行数:186
完成实践投入的时间(小时数):1
资料查阅时间:0.5
编程调试时间:0.5
测试数据设计:
输入:
1 3
2 1
输出:
每次结果可能不一样,下面是一次的结果
但多次测试,iAccnt1 + iAccnt2 均为0。
测试结果分析:
多次测试,每次Account1与Account2之和均为0,表示两线程没有同时进入临界区,Dekker算法正确地解决了临界区互斥的问题。
实践体会:
实践体会和收获
复习了操作系统概念中与同步有关的内容,有了进一步理解。
参考文献:
[1] Jeffrey Richter著,王建华等译.Windows核心编程(第四版)[M].北京:机械工业出版社出版社. 2000
[2] Abraham Silberschatz ,Peter Baer Galvin and Greg Gagne. 郑扣根等译.操作系统概念 第六版,翻译版. 北京:高等教育出版社. 2004
任务3:使用Win32所提供的同步对象解决兄弟问题
实践目标:
学会使用Win32提供的同步对象解决临界区问题
实践内容:
使用Win32所提供的同步对象——互斥体对象——解决兄弟问题
设计思路:
- Windows提供了多种同步对象,如Mutex, Event, Semaphore等,还有用户态下的Critical Section。
- Critical Section使用方便,速度快,对此问题很适用。
- 在任务二的基础上,将软件方法实现的临界区保护改为Critical Section即可。
主要数据结构:
//线程参数
typedef struct BrotherInfo {
int iSerial;
DWORD dwDelay;
} BROTHERINFO,*PBROTHERINFO;
CRITICAL_SECTION g_cs;//全局
void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection);
void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection);
主要代码分析:
在Banke中初始化临界区对象:InitializeCriticalSection(&cs);
在Banke中删除临界区对象: DeleteCriticalSection(&cs);
/*Brother函数仍然是关键
*/
DWORD WINAPI Brother(LPVOID lpParam){
int iLoan, iAccnt,iCounter = 0;
……
do {
iLoan = rand();
EnterCriticalSection(&cs);//进入临近区
iAccnt1 = iAccnt1 - iLoan;
iAccnt2 = iAccnt2 + iLoan;
iAccnt = iAccnt1 + iAccnt2;
LeaveCriticalSection(&cs);//离开临近区
iCounter++;
} while ( (iAccnt == 0) && (iCounter<10));
……
} //End of Brother
基本数据:
源程序代码行数:172
完成实践投入的时间(小时数):1
资料查阅时间: 0.5
编程调试时间:0.5
测试数据设计:
输入:
1 1
2 3
输出:
每次结果可能不一样,下面是一次的结果
但多次测试,iAccnt1 + iAccnt2 均为0。
测试结果分析:
多次测试,每次Account1与Account2之和均为0,说明Critical Section可以解决临界区问题。
实践体会:
实践体会和收获
对Windows提供的同步对象有了更清晰的认识,体会到其方便。
参考文献:
[1] Jeffrey Richter著,王建华等译.Windows核心编程(第四版)[M].北京:机械工业出版社出版社. 2000
任务4:用Win32所提供的同步对象解决有限缓冲区问题
实践目标:
学会用Win32所提供的同步对象解决有限缓冲区问题
实践内容:
写一个多线程实现C/C++语言程序:一些线程负责找出某个数据范围的素数,并放到一个数组中,另一些线程负责将数组中的素数按次序取出,显示出来。
要求定义一个全局变量的数组:int prime[9] 用于存放找到的待显示的素数,要理解成“环形缓冲区”。
设计思路:
分析:
- 生产者之间互斥;
- 消费者间互斥;
- 生产者、消费者间同步。
- 生产者全部退出要通知消费者;反之亦然。
解决:
1. 对1,Critical Section可解决
2. 对2,同上
3. 对3,一对同步信号量:hFull(初始0),hEmpty(初始缓冲区大小)
4. 对4,两个手动的Event,可通知所以等待线程
5. 另外,WaitForMultiObjects可以做“或”使用
主要数据结构:
typedef struct{//定义缓冲区
INT q[QLEN];
int front,rear;
}ProdQueue,*PProdQueue;
// 清空缓冲区
inline VOID ClearQueue(PProdQueue pQ){
pQ->front = pQ->rear = 0;
}
//是否缓冲区满
inline BOOL IsFullProductQueue(PProdQueue pQ){
return (pQ->rear+1)%QLEN == pQ->front;
}
//是否缓冲区空
inline BOOL IsEmptyProuctQueue(PProdQueue pQ){
return pQ->front == pQ->rear;
}
//放一个素数
inline VOID PutProduct(PProdQueue pQ,INT num){
pQ->q[pQ->rear] = num;
pQ->rear = (pQ->rear+1)%QLEN;
}
//取一个素数
inline INT TakeProduct(PProdQueue pQ){
INT ret = pQ->q[pQ->front];
pQ->front = (pQ->front+1)%QLEN;
return ret;
}
ProdQueue prodQ;//定义缓冲区
typedef struct { //线程参数
int iThreadId; //用户定义的线程ID
char cTag; //标识该线程是放素数线程,还是取素数线程
int iA;//若为放素数线程,表示搜索的起始数字;若为取素数线程,表示取素数的个数
int iB;//若为放素数线程,表示素数个数;若为取素数线程,该数无意义
} INFO, *PINFO;
INT nMaxConsumer//消费者线程的数目
INT nMaxProducer; //生产者线程的数目
HANDLE hEmpty, hFull;//一对同步信号量的句柄,分析中对3的解决
HANDLE hEventAllConsumerDead,hEventAllProducerDead;//分析中对4的解决,两个事件的句柄
主要代码结构 :
对生产者:
/*控制当所有消费者退出时,生产者也应退出(装满缓冲区或已产生足够多的素数)
*/
DWORD WINAPI ControlProducer(LPVOID lpParam){
……
WaitForMultipleObjects(nMaxConsumer,hConsumer,TRUE,INFINITE);//等所有消费者死
SetEvent(hEventAllConsumerDead);//使所有消费者死的消息事件有信号
……
}
DWORD WINAPI SingleProducer(LPVOID lpParam){
……
while(1){
……//产生素数
WaitForMultipleObjects(“有空”信号量有信号||“所有消费者死”事件有信号);
If(“所有消费者死”事件有信号){
……//填充一个素数后继续循环,为的是给其它线程一个机会,注意保护临界区
}else{
……//放素数,释放一个满的信号量,注意保护临界区
}
}
}
至于消费者,类似。
主要代码分析:
//函数ReaderPriority(总控)主要部分
//部分初始化
while ( inFile>>thread_info[n_thread].serial) {
inFile>>thread_info[n_thread].entity;
inFile>>thread_info[n_thread].beg;
inFile>>thread_info[n_thread].len;
//创建后,线程处于阻塞状态
if (thread_info[n_thread].entity == 'D'){
hConsumer[nMaxConsumer++] = CreateThread(NULL,0,SingleConsumer,&thread_info[n_thread],CREATE_SUSPENDED,&thread_ID);
}
else{
hProducer[nMaxProducer++] = CreateThread(NULL,0,SingleProducer,&thread_info[n_thread],CREATE_SUSPENDED,&thread_ID);
}
++n_thread;
inFile.get();
}
//一对同步信号量,控制生产者、消费者同步
hEmpty = CreateSemaphore(NULL,QLEN-1,QLEN-1,"empty");
hFull = CreateSemaphore(NULL,0,QLEN-1,"full");
//两个控制线程,其中等待所有生产者、消费者,并使相关事件有信号
hCtrlConsumer = CreateThread(NULL,0,ControlConsumer,NULL,0,&thread_ID);
hCtrlProducer = CreateThread(NULL,0,ControlProducer,NULL,0,&thread_ID);
//创建这两个“全死”的事件
hEventAllConsumerDead = CreateEvent(NULL,TRUE,FALSE,"all_consumer_dead");
hEventAllProducerDead = CreateEvent(NULL,TRUE,FALSE,"all_producer_dead");
//全生产者、消费者可调度
for (i=0; i<nMaxConsumer;++i){
ResumeThread(hConsumer[i]);
}
for (i=0; i<nMaxProducer;++i){
ResumeThread(hProducer[i]);
}
WaitForSingleObject(hCtrlConsumer,INFINITE);
WaitForSingleObject(hCtrlProducer,INFINITE);
……//一系列清理工作
DWORD WINAPI ControlProducer(LPVOID lpParam){
HANDLE hEmpty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"empty");
HANDLE hEventAllConsumerDead = OpenEvent(EVENT_ALL_ACCESS,FALSE,"all_consumer_dead");
if (nMaxConsumer > 0)
WaitForMultipleObjects(nMaxConsumer,hConsumer,TRUE,INFINITE);//等所有消费者退出
printf("all consumer threads end/n");
SetEvent(hEventAllConsumerDead); //当所有消费者退出时,使相关事件有信号
return 1;
}
DWORD WINAPI SingleProducer(LPVOID lpParam){
HANDLE hEmptyOrConsumerDead[2];//[0]是“有空”信号量的句柄,[1]是“所有消费者退出”事件有效
hEmptyOrConsumerDead[0] = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"empty");
hEmptyOrConsumerDead[1] = OpenEvent(EVENT_ALL_ACCESS,FALSE,"all_consumer_dead");
HANDLE hFull = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"full");
INT nPrimeProduced = 0;
INT iPrimeFound;
BOOL qFull=FALSE;
PThreadInfo pThreadInfo = (PThreadInfo)lpParam;
while(1){
//下面产生一个素数,如果总数已经够规定数目就退出
if (nPrimeProduced == pThreadInfo->len)
break;
while(isPrime[pThreadInfo->beg]==FALSE)
++pThreadInfo->beg;
iPrimeFound = pThreadInfo->beg;
++pThreadInfo->beg;
++nPrimeProduced;
DWORD idx = WaitForMultipleObjects(2,hEmptyOrConsumerDead,FALSE,INFINITE)-WAIT_OBJECT_0;//无效等待,直到信号量或事件有信号,并得到具体是哪个
if (idx == 1){//说明所有消费都退出,同步信号量也就没什么意义了,放一素数到缓冲区后,继续循环,等下一次调度
EnterCriticalSection(&csProducer);
if (IsFullProductQueue(&prodQ))
qFull = TRUE;
else{
PutProduct(&prodQ,iPrimeFound);
printf("Thread %2d produced %5d ,and puts into queue/n",pThreadInfo->serial,iPrimeFound);
}
LeaveCriticalSection(&csProducer);
if (qFull){
break;
}
else{
continue;
}
}else{//正常的生产者、消费者解决方式
EnterCriticalSection(&csProducer);
PutProduct(&prodQ,iPrimeFound);
printf("Thread %2d produced %5d ,and puts into queue/n",pThreadInfo->serial,iPrimeFound);
LeaveCriticalSection(&csProducer);
ReleaseSemaphore(hFull,1,NULL);
}
}
printf("Thread %d exits/n",pThreadInfo->serial);
return EXIT_SUCCESS;
}
对消费者类似的,不再详细注释
DWORD WINAPI ControlConsumer(LPVOID lpParam){
HANDLE hFull = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,_T("full"));
HANDLE hEventAllProducerDead = OpenEvent(EVENT_ALL_ACCESS,FALSE,"all_producer_dead");
if (nMaxProducer > 0)
WaitForMultipleObjects(nMaxProducer,hProducer,TRUE,INFINITE);
printf("all producer threads end/n");
if(SetEvent(hEventAllProducerDead)==0){
printf("set event failed/n");
printf("error code :%d/n",GetLastError());
}
return 1;
}
DWORD WINAPI SingleConsumer(LPVOID lpParam){
PThreadInfo pThreadInfo = (PThreadInfo)lpParam;
HANDLE hFullOrProducerDead[2];
HANDLE hEmpty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"empty");
hFullOrProducerDead[0] = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"full");
hFullOrProducerDead[1] = OpenEvent(EVENT_ALL_ACCESS,FALSE,"all_producer_dead");
BOOL isEmpty = FALSE;
int iConsumed = 0;
while(1){
if(iConsumed == pThreadInfo->beg)
break;
++iConsumed;
DWORD idx = WaitForMultipleObjects(2,hFullOrProducerDead,FALSE,INFINITE)-WAIT_OBJECT_0;
if (idx == 1){
EnterCriticalSection(&csConsumer);
if (IsEmptyProuctQueue(&prodQ))
isEmpty = TRUE;
else
printf("Thread %2d take %5d from queue/n",pThreadInfo->serial,TakeProduct(&prodQ));
LeaveCriticalSection(&csConsumer);
if(isEmpty){
break;
}
else{
continue;
}
}
EnterCriticalSection(&csConsumer);
printf("Thread %2d take %5d from queue/n",pThreadInfo->serial,TakeProduct(&prodQ));
LeaveCriticalSection(&csConsumer);
ReleaseSemaphore(hEmpty,1,NULL);
}
printf("Thread %d exits/n",pThreadInfo->serial);
return EXIT_SUCCESS;
}
基本数据:
源程序代码行数:274
完成实践投入的时间(小时数):2.5
设计时间: 1
资料查阅时间:0.5
编程调试时间:1
测试数据设计:
测试数据1://只有生产者
输入:
1 W 10 10
2 W 1 10
输出:
测试数据2://只有消费者
输入:
1 D 3 0
2 D 3 0
输出:
测试数据3://生产者产生素数个数少于消费者需求数(供不应求)
输入:
1 W 10 2
2 W 1 3
3 D 4 0
4 D 3 0
输出:
测试数据4://供大于求,且最终放满缓冲区
输入:
1 W 10 10
2 W 1 10
3 D 3 0
4 D 3 0
输出:
测试数据5://供大于求,且最终放不满缓冲区
输入:
1 W 10 5
2 W 1 5
4 W 3 2
5 D 2 0
6 D 3 0
输出:
测试数据6://供求恰相等
输入:
1 W 10 2
2 W 5 3
3 W 4 2
4 D 5 0
5 D 2 0
输出:
测试结果分析:
生产者之间、消费者之间自由竞争缓冲区,生产者与消费者做到了同步(没有缓冲区无产品仍有消费者取走产品或产品多于缓冲区大小的情况)。
当只有生产者时,尽量放满缓冲区后,能够正常结束;只有消费者时,取走所需产品或取空缓冲区后,能够正常结束。
结果与设计相符,与要求相符,与手动分析相符。
实践体会:
考虑真实的情况,正确的逻辑(设计的基础)是关键,编码只是思路的表达。
对Windows提供的常用同步对象的综合应用更加熟练。
参考文献:
[1] Jeffrey Richter著,王建华等译.Windows核心编程(第四版)[M].北京:机械工业出版社出版社. 2000
任务5:用Win32同步对象实现P、V操作,解决兄弟问题
实践目标:
学会用Win32所提供的同步对象实现你自己的P、V操作,并使用它们并解决兄弟问题
实践内容:
利用Win32所提供的时间对象实现自己的PV 操作,并解决兄弟问题
设计思路:
从二值的P、V到一般的P、V操作可由Binary Semaphores算法实现; 而二值的P、V操作可由事件对象(Event)实现。
主要数据结构及主要代码分析:
这也是本任务的关键,我将信号量用类实现.
对于如何实现P、V,Binary Semaphores算法已解决这个问题。
根据此,有如下实现
class MSemaphore{
LONG C;
HANDLE m_hS1,m_hS2;
public:
MSemaphore(LONG nSem=1,PTSTR pSemId=NULL){
C = nSem;
m_hS1 = CreateEvent(NULL,FALSE,TRUE,pSemId);
m_hS2 = CreateEvent(NULL,FALSE,FALSE,pSemId);
}
~MSemaphore(){
CloseHandle(m_hS1);
CloseHandle(m_hS2);
}
void P(){
WaitForSingleObject(m_hS1,INFINITE);
InterlockedDecrement(&C);
if (C < 0) {
SetEvent(m_hS1);
WaitForSingleObject(m_hS2,INFINITE);
}
SetEvent(m_hS1);
}
void V(){
WaitForSingleObject(m_hS1,INFINITE);
InterlockedIncrement(&C);
if (C <= 0)
SetEvent(m_hS2);
else
SetEvent(m_hS1);
}
};
用来解决兄弟问题的关键部分:
MSemaphore mSem(1,_T("com.gene"));//定义一个实现的信号量的对象
mSem.P();//临界区前的P操作
iAccnt1 = iAccnt1 - iLoan;
iAccnt2 = iAccnt2 + iLoan;
iAccnt = iAccnt1 + iAccnt2;
mSem.V();//临界区后的V操作
基本数据:
源程序代码行数:199
完成实践投入的时间(小时数):1
资料查阅时间:0.5
编程调试时间:0.5
测试数据设计:
输入:
1 1
2 3
输出:
测试结果分析:
各“兄弟”线程之间自由竞争iAccnt1和iAccnt2资源,能够正确的处理临界区问题。
实践体会:
事实上,如果像模仿Critical Section的实现可以做的更好。
参考文献:
[1] Abraham Silberschatz ,Peter Baer Galvin and Greg Gagne. 郑扣根等译.操作系统概念 第六版,翻译版. 北京:高等教育出版社. 2004
任务6:采用动态优先数,编写一进程调度程序模拟程序
实践目标:
理解采用动态优先数的进程调度算法,并复习链表的操作。
实践内容:
采用动态优先数,编写一进程调度程序模拟程序。
设计思路:
需要几个地方模拟:
1. 调度任务产生的模拟,应注意时间上的先后
2. 调度队列的实现
3. 任务完成后,为方便信息输出,可暂存于别处。
还有一个要注意的地方是,动态优先级的问题,应该设置一个范围,比如Linux中nice值为:-20 ~ +19,本例中不妨设最低0(最高是在产生任务时确定,具体实现可以通过取模)。
主要数据结构:
class CPCB {
public:
int ID;//标识,系统分配
int createTime;//创建时间
int runTime;//要求时间
int priority;//动态优先数
int status;//状态――就绪、结束
int waittime;//等待时间
int turnovertime;//周转时间
int remaintime;//剩余运行时间
CPCB *next;//下一PCB指针
CPCB();
virtual ~CPCB();
};
CPCB *mhead,//所有的任务按时间排于此
*task_over_list,//为保存结果,调度结束后存放于此
*head;//所有在排队的任务按优先级排于此队
主要代码分析:
CPCB *mhead,//模拟作业调度的结果
*task_over_list;//为保存结果,调度结束后存放于此
CPCB *p,*next,*pre;
mhead = NULL;//初始时,没有任务
for(int i=0;i<m_processNum;i++) {//模拟现实,产生一些任务,最终按时间排列
CPCB *p=InitOnePro();//随机产生一个任务,不对时间作假设
p->status=0;
//下面将产生的任务,插入有序链表
if(mhead==NULL){
mhead=p;
mhead->next=NULL;
}else{
if(mhead->createTime>p->createTime){
p->next = mhead;
mhead = p;
}else{
pre = mhead;
next = pre->next;
while(next){
if(p->createTime<=next->createTime)
break;
pre = next;
next = next->next;
}
pre->next = p;
p->next = next;
}
}
}
……
//调度,关键部分
int cpuCurrTime=mhead->createTime;//假设前面已经没有需要调度的,it's a new start.
head = NULL;//CPU队列为空
task_over_list = NULL;//结束任务也为空
while (1){
if (mhead == NULL && head == NULL)break;//说明所有任务都处理完,且无新任务,此时CPU实际在运行Idle进程,此处简单假设
while (mhead && mhead->createTime == cpuCurrTime){//下面这一段,将这一时刻到达的新任务按优先数挂在就绪队列上
p = mhead;
mhead = mhead->next;
if (head == NULL){
head = p;
head->next = NULL;
}else{
if (p->priority >= head->priority){
p->next = head;
head = p;
}else{//若==,则任务同时到达、同优先数,不作任何假设
pre = head;
next = pre->next;
while (next){
if (p->priority>next->priority)
break;
pre = next;
next = next->next;
}
pre->next = p;
p->next = next;
}
}
}
if (head == NULL){//就绪队列为空,此时CPU空闲(Idle)
++cpuCurrTime;
continue;
}
head->remaintime -=1;
//下面这句表现CPU工作过程(实际是划一条线在图形界面上) DrawLine(500+cpuCurrTime*14,60+(head->ID-1)*30,500+(cpuCurrTime+1)*14,60+(head->ID-1)*30);
++cpuCurrTime;//假设一个时间片长度
if(head->remaintime==0){//此任务处理完毕,将移至完成队列(为方便表示)
head->status=1;//设标志
head->waittime=(cpuCurrTime-head->createTime)-head->runTime;
head->turnovertime=cpuCurrTime-head->createTime;
CPCB *tmp = head;
head = head->next;
//delete tmp;
tmp->next = task_over_list;
task_over_list = tmp;
}else{{//进程运行一次后,若要求运行时间>0,则再将它加入队列(以新的优先级)
p = head;//先删去此任务
head = head->next;
p->priority -= 1;
if (p->priority < 0)?
p->priority = 0;
//insert into queue again
if (head == NULL){
head = p;
head->next = NULL;
}else
if (p->priority > head->priority){
p->next = head;
head = p;
}else{//若==,则任务同时到达、同优先数,不作任何假设
pre = head;
next = pre->next;
while (next){
if (p->priority>next->priority)
break;
pre = next;
next = next->next;
}
pre->next = p;
p->next = next;
}
}
}
}
……//还有一些相关计算及界面上的表示
}
基本数据:
源程序代码行数:210左右
完成实践投入的时间(小时数):3
资料查阅时间:0.5
编程调试时间:2.5
测试数据设计:
随机产生,以下是一次运行时产生的数据及结果
测试结果分析:
以上面测试为例,手动的、按时间一步步分析,结果不会出现低优先数进程得到调度的情况。
实践体会:
现在多核CPU已经很常见了,如果在模拟时加上对CPU亲和性的考虑等会更有趣.
参考文献:
[1] Jeffrey Richter著,王建华等译.Windows核心编程(第四版)[M].北京:机械工业出版社出版社. 2000