-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
791 lines (576 loc) · 57.8 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 7.2.0">
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
<link rel="mask-icon" href="/images/logo.svg" color="#222">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
<script id="hexo-configurations">
var NexT = window.NexT || {};
var CONFIG = {"hostname":"example.com","root":"/","scheme":"Muse","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
</script>
<meta name="description" content="笔记记录">
<meta property="og:type" content="website">
<meta property="og:title" content="wqllyx的网站">
<meta property="og:url" content="http://example.com/page/2/index.html">
<meta property="og:site_name" content="wqllyx的网站">
<meta property="og:description" content="笔记记录">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="风">
<meta property="article:tag" content="计算机">
<meta name="twitter:card" content="summary">
<link rel="canonical" href="http://example.com/page/2/">
<script id="page-configurations">
// https://hexo.io/docs/variables.html
CONFIG.page = {
sidebar: "",
isHome : true,
isPost : false,
lang : 'zh-CN'
};
</script>
<title>wqllyx的网站</title>
<noscript>
<style>
.use-motion .brand,
.use-motion .menu-item,
.sidebar-inner,
.use-motion .post-block,
.use-motion .pagination,
.use-motion .comments,
.use-motion .post-header,
.use-motion .post-body,
.use-motion .collection-header { opacity: initial; }
.use-motion .site-title,
.use-motion .site-subtitle {
opacity: initial;
top: initial;
}
.use-motion .logo-line-before i { left: initial; }
.use-motion .logo-line-after i { right: initial; }
</style>
</noscript>
</head>
<body itemscope itemtype="http://schema.org/WebPage">
<div class="container use-motion">
<div class="headband"></div>
<header class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-container">
<div class="site-nav-toggle">
<div class="toggle" aria-label="切换导航栏">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
</div>
<div class="site-meta">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<h1 class="site-title">wqllyx的网站</h1>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<div class="site-nav-right">
<div class="toggle popup-trigger">
</div>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="main-menu menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>
</li>
</ul>
</nav>
</div>
</header>
<div class="back-to-top">
<i class="fa fa-arrow-up"></i>
<span>0%</span>
</div>
<main class="main">
<div class="main-inner">
<div class="content-wrap">
<div class="content index posts-expand">
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2024/06/03/c-%E8%AE%BE%E8%AE%A1%E7%BA%BF%E7%A8%8B%E6%B1%A0%EF%BC%881%EF%BC%89-%E9%94%81/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="风">
<meta itemprop="description" content="笔记记录">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="wqllyx的网站">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2024/06/03/c-%E8%AE%BE%E8%AE%A1%E7%BA%BF%E7%A8%8B%E6%B1%A0%EF%BC%881%EF%BC%89-%E9%94%81/" class="post-title-link" itemprop="url">c++ 设计线程池(1)----锁</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2024-06-03 10:25:40 / 修改时间:10:26:19" itemprop="dateCreated datePublished" datetime="2024-06-03T10:25:40+08:00">2024-06-03</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="线程池的好处"><a href="#线程池的好处" class="headerlink" title="线程池的好处"></a>线程池的好处</h2><p>线程的开启也是一个相对缓慢的过程。</p>
<ul>
<li>如果不用线程池,在系统运行过程中实时开启线程,销毁线程。会极大增加系统的负载。导致系统的实时性减低,业务处理能力也会降低</li>
<li>服务器启动时,就事先创建好线程池,当业务来到时,直接从线程池取出一个线程来执行任务。任务完成后直接把线程归还给池,用于后续重复使用。</li>
</ul>
<h2 id="线程池的模式"><a href="#线程池的模式" class="headerlink" title="线程池的模式"></a>线程池的模式</h2><ul>
<li>固定大小</li>
<li>可变大小</li>
</ul>
<h2 id="线程同步"><a href="#线程同步" class="headerlink" title="线程同步"></a>线程同步</h2><p>并发编程的基本问题:</p>
<ul>
<li>互斥</li>
<li>通信</li>
</ul>
<p>我们希望有一段代码被<strong>原子式</strong>执行,由于单处理器的任务切换(中断)或者多处理器的并行,我们不能保证。</p>
<p>所以需要一种机制来保证原子性—–锁</p>
<p>操作系统给上层提供了一个API,使得应用程序可以使用锁来达到代码执行的原子性。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">lock_t</span> mutex; <span class="comment">// some globally-allocated lock 'mutex'</span></span><br><span class="line">...</span><br><span class="line"><span class="built_in">lock</span>(&mutex);</span><br><span class="line">... <span class="comment">// 临界区</span></span><br><span class="line"><span class="built_in">unlock</span>(&mutex);</span><br></pre></td></tr></table></figure>
<p>调用者调用lock尝试获取锁,如果没有其他现场持有锁,线程就会获得锁,进入临界区。否则,他就会阻塞。直到锁可用。(具体实现比这复杂)</p>
<h3 id="锁的实现原理"><a href="#锁的实现原理" class="headerlink" title="锁的实现原理"></a>锁的实现原理</h3><p>锁定实现要求:</p>
<ul>
<li> 提供互斥(保证临界区执行的原子性)</li>
<li>公平性(不会有饿死线程)</li>
<li>性能</li>
</ul>
<h4 id="方式1:控制中断"><a href="#方式1:控制中断" class="headerlink" title="方式1:控制中断"></a>方式1:控制中断</h4><p>缺点:</p>
<ul>
<li>需要应用进程有特权操作,因为中断需要特权操作</li>
<li>只适合单处理器,多处理器无法保证互斥</li>
<li>会导致中断丢失</li>
<li>效率低。</li>
</ul>
<h4 id="方式-2:自旋锁"><a href="#方式-2:自旋锁" class="headerlink" title="方式 2:自旋锁"></a>方式 2:自旋锁</h4><p>需要硬件指令支持:<strong>test and set 指令 (原子交换)</strong></p>
<blockquote>
<p><strong>假设无硬件支持</strong>,我们很容易想到的一个锁(就是变量)实现是:(测试和设置不是原子操作)</p>
</blockquote>
<p>使用一个变量来标志锁是否被占有。这样进程在进入临界区时,会:</p>
<ul>
<li>检查标志,如果可以进入就进入临界区吗,设置锁标志。不能就阻塞等待。</li>
<li>在退出临界区时,清除标志。</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">lock_t</span> { <span class="type">int</span> flag; } <span class="type">lock_t</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">(<span class="type">lock_t</span> *mutex)</span> </span>{</span><br><span class="line"><span class="comment">// 0 -> lock is available, 1 -> held</span></span><br><span class="line">mutex->flag = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">lock</span><span class="params">(<span class="type">lock_t</span> *mutex)</span> </span>{</span><br><span class="line"><span class="keyword">while</span> (mutex->flag == <span class="number">1</span>) <span class="comment">// TEST the flag</span></span><br><span class="line">; <span class="comment">// spin-wait (do nothing)</span></span><br><span class="line">mutex->flag = <span class="number">1</span>;</span><br><span class="line"><span class="comment">// now SET it!</span></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">unlock</span><span class="params">(<span class="type">lock_t</span> *mutex)</span> </span>{</span><br><span class="line">mutex->flag = <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>不能保证互斥:</p>
<ul>
<li><p>如果线程1在测试时,被中断到线程2,</p>
</li>
<li><p>线程2执行测试并成功设置了标志。此时又被中断</p>
</li>
<li><p>线程1,此时测试也会成功。两者同时进入了临界区</p>
</li>
</ul>
<p>性能问题:</p>
<p>自旋会导致白白浪费CPU资源</p>
<blockquote>
<p>test and set 硬件(原子交换)指令,我们可以实现一个满足要求的最简单的锁—-自旋锁</p>
</blockquote>
<p>test and set 的原理:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">// 这就是一个交换操作,只不过硬件保证了他的原子性。</span><br><span class="line">int TestAndSet(int *old_ptr, int new) {</span><br><span class="line"></span><br><span class="line">int old = *old_ptr; // 获取旧值</span><br><span class="line">*old_ptr = new;// 设置新值</span><br><span class="line">return old;// 返回旧值。</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>自旋锁的实现:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">lock_t</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> flag;</span><br><span class="line">} <span class="type">lock_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">(<span class="type">lock_t</span> *lock)</span> </span>{</span><br><span class="line"><span class="comment">// 0 indicates that lock is available, 1 that it is held</span></span><br><span class="line"> lock->flag = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">lock</span><span class="params">(<span class="type">lock_t</span> *lock)</span> </span>{</span><br><span class="line"> <span class="keyword">while</span> (<span class="built_in">TestAndSet</span>(&lock->flag, <span class="number">1</span>) == <span class="number">1</span>); <span class="comment">// spin-wait (do nothing)</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">unlock</span><span class="params">(<span class="type">lock_t</span> *lock)</span> </span>{</span><br><span class="line"> lock->flag = <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>工作场景模拟:</p>
<ul>
<li><p>当调用lock时,如果没有线程持有锁,flag=0,原子交换指令flag=1,返回旧flag=0,循环为假,跳出,获得锁。(这是原子操作,不会被中断)</p>
</li>
<li><p>如果在执行完原子指令之后,被中断(此时flag=1,)别的线程如果执行原子交换指令,while循环一致为真,导致自旋。保证了互斥</p>
</li>
<li><p>只有在第一个线程结束执行,调用unlock时,才会解除自旋。</p>
</li>
</ul>
<p>自旋锁:</p>
<p>保证了互斥,但没有保证公平。</p>
<p>有可能某个线程会一直自旋下去,导致饿死。</p>
<blockquote>
<p>compare-and-exchange 硬件支持的原子指令</p>
</blockquote>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果满足期望的值,则会将锁交换。否则,什么也不做。返回锁原来的值。</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">CompareAndSwap</span><span class="params">(<span class="type">int</span> *ptr, <span class="type">int</span> expected, <span class="type">int</span> <span class="keyword">new</span>)</span> </span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="type">int</span> actual = *ptr;</span><br><span class="line"> <span class="keyword">if</span> (actual == expected)</span><br><span class="line"> *ptr = <span class="keyword">new</span>;</span><br><span class="line"> <span class="keyword">return</span> actual;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>使用这条指令来实现自旋锁和上面的指令差不多:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">lock</span><span class="params">(<span class="type">lock_t</span> *lock)</span> </span>{</span><br><span class="line"> <span class="keyword">while</span> (<span class="built_in">CompareAndSwap</span>(&lock->flag, <span class="number">0</span>, <span class="number">1</span>) == <span class="number">1</span>); <span class="comment">// spin</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>在无等待同步(wait-free synchronization)时,这个指令比test and set强大、</p>
<blockquote>
<p>链接的加载(load-linked)和条件式存储(store-conditional)指令 原子指令</p>
</blockquote>
<p>连接加载:从内存中取出一个值到寄存器,(可以理解为解引用一个地址。)</p>
<p>条件式存储:只有上次从地址A取出的值,自从上次取出之后,没有被人家更改过(也就是说这个值还是那个值),才会去操作这个地址。)</p>
<h4 id="ticket-锁"><a href="#ticket-锁" class="headerlink" title="ticket 锁"></a>ticket 锁</h4><blockquote>
<p>获取并增加fetch-and-add 原子指令 </p>
</blockquote>
<p>它能原子地返回特定地址的旧值,并且让该值自增一。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">FetchAndAdd</span><span class="params">(<span class="type">int</span> *ptr)</span> </span>{</span><br><span class="line"> <span class="type">int</span> old = *ptr;</span><br><span class="line"> *ptr = old + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">return</span> old;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">lock_t</span> {</span><br><span class="line"> <span class="type">int</span> ticket;<span class="comment">//车票</span></span><br><span class="line"> <span class="type">int</span> turn; <span class="comment">//(轮到自己)</span></span><br><span class="line">} <span class="type">lock_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">lock_init</span><span class="params">(<span class="type">lock_t</span> *lock)</span> </span>{</span><br><span class="line"> lock->ticket = <span class="number">0</span>;</span><br><span class="line"> lock->turn <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">lock</span><span class="params">(<span class="type">lock_t</span> *lock)</span> </span>{</span><br><span class="line"> <span class="type">int</span> myturn = <span class="built_in">FetchAndAdd</span>(&lock->ticket);</span><br><span class="line"> <span class="keyword">while</span> (lock->turn != myturn); <span class="comment">// spin</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">unlock</span><span class="params">(<span class="type">lock_t</span> *lock)</span> </span>{</span><br><span class="line"> <span class="built_in">FetchAndAdd</span>(&lock->turn);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>相当于先买票,再排队。</p>
<p>使用两个变量,构建锁:</p>
<p>获取锁的方式:</p>
<ul>
<li>先买票(调用获取并增加指令),此时自己的lock->ticket 为1,表示自己有票了。</li>
<li>再去排队(while循环),如果轮到自己了,就可以获得锁。</li>
</ul>
<p><strong>既可以保证互斥也可以保证公平性。</strong></p>
<h4 id="自旋过长时间的应对方法-—-yield-系统调用"><a href="#自旋过长时间的应对方法-—-yield-系统调用" class="headerlink" title="自旋过长时间的应对方法 —- yield()系统调用"></a>自旋过长时间的应对方法 —- yield()系统调用</h4><p>在以上公平互斥锁的基础上,还有一个问题:</p>
<p>如果处于临界区中的线程被中断,其他线程就得长时间自旋等待。</p>
<p>解决办法:</p>
<ul>
<li>yield()系统调用:在线程发现锁被别人持有时,主动调用yield让出锁,是自己进入就绪态。</li>
</ul>
<p>在两个线程下工作的很好。</p>
<p>缺点:如果在许多线程下反复竞争一把锁时,一个线程长时间持有锁,会导致其他线程全部让出锁,知道持有锁的继续执行。</p>
<h4 id="解决让出过多问题-—-使用等待队列,睡眠代替自旋。"><a href="#解决让出过多问题-—-使用等待队列,睡眠代替自旋。" class="headerlink" title="解决让出过多问题 —- 使用等待队列,睡眠代替自旋。"></a>解决让出过多问题 —- 使用等待队列,睡眠代替自旋。</h4><p>前面一些方法的真正问题是存在太多的偶然性。调度程序决定如何调度。如果调度不合理,线程或者一直自旋(第一种方法),或者立刻让出 CPU(第二种方法)。无论哪种方法,都可能造成浪费,也能防止饿死。</p>
<p>因此,我们必须<strong>显式地施加某种控制,决定锁释放时,谁能抢到锁(排队)</strong>。为了做到这一点,我们需要操作系统的更多支持,并需要一个队列来保存等待锁的线程</p>
<p>睡眠与唤醒。</p>
<p>一个线程如果尝试获得锁,失败,自己会陷入睡眠。等到获得锁的线程结束执行时,他会唤醒正在睡眠的线程队列。队列中如果没有人有紧急情况,就会按排队顺序接着下一个线程执行。</p>
<p>例如,Linux 提供了 futex,具体来说,每个 futex 都关联一个特定的物理内存位置,也有一个事先建好的内核队列。调用者通过futex 调用(见下面的描述)来睡眠或者唤醒。<br>具体来说,有两个调用。调用 futex_wait(address, expected)时,如果 address 处的值等于expected,就会让调线程睡眠。否则,调用立刻返回。调用 futex_wake(address)唤醒等待队列中的一个线程。图 28.9 是 Linux 环境下的例子:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">mutex_lock</span><span class="params">(<span class="type">int</span> *mutex)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="type">int</span> v;</span><br><span class="line"> <span class="comment">/*首先测试mutixc值为是否0,如果是,说明没人等待,并且锁是空闲的。也就是说只有我自己想要锁。那我直接拿。并把最高位设置为1.*/</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">atomic_bit_test_set</span>(mutex, <span class="number">31</span>) == <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 否则,等待者加1.</span></span><br><span class="line"> <span class="built_in">atomic_increment</span>(mutex);</span><br><span class="line"> <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 再次循环尝试获得锁。</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">atomic_bit_test_set</span>(mutex, <span class="number">31</span>) == <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 如果,获得了,等待者减一。</span></span><br><span class="line"> <span class="built_in">atomic_decrement</span>(mutex);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* We have to wait now. First make sure the futex value</span></span><br><span class="line"><span class="comment"> we are monitoring is truly negative (i.e. locked).*/</span></span><br><span class="line"> v = *mutex;</span><br><span class="line"> <span class="keyword">if</span> (v >= <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> <span class="comment">// 如果执行到这,说明锁已经没了,我们需要等待。</span></span><br><span class="line"> <span class="built_in">futex_wait</span>(mutex, v);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">mutex_unlock</span><span class="params">(<span class="type">int</span> *mutex)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">/* Adding 0x80000000 to the counter results in 0 if and only if</span></span><br><span class="line"><span class="comment"> there are not other interested threads */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">atomic_add_zero</span>(mutex, <span class="number">0x80000000</span>))</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> <span class="comment">/* There are other threads waiting for this mutex,</span></span><br><span class="line"><span class="comment"> wake one of them up.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="built_in">futex_wake</span>(mutex);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这段代码来自 nptl 库(gnu libc 库的一部分)[L09]中 lowlevellock.h,它很有趣。基本上,它利用一个整数,同时记录锁是否被持有(整数的最高位),以及等待者的个数(整数的其余所有位)。因此,如果锁是负的,它就被持有(因为最高位被设置,该位决定了整数的符号)。这段代码的有趣之处还在于,它展示了如何优化常见的情况,即没有竞争时:只有一个线程获取和释放锁,所做的工作很少(获取锁时测试和设置的原子位运算,释放锁时原子的加法)。</p>
<h4 id="两阶段锁"><a href="#两阶段锁" class="headerlink" title="两阶段锁"></a>两阶段锁</h4><p>两阶段锁的第一阶段会先自旋一段时间,希望它可以获取锁。<br>如果第一个自旋阶段没有获得锁,第二阶段调用者会睡眠,直到锁可用。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2024/06/03/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8E%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%EF%BC%881%EF%BC%89-%E4%BD%95%E4%B8%BA%E5%B9%B6%E5%8F%91/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="风">
<meta itemprop="description" content="笔记记录">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="wqllyx的网站">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2024/06/03/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8E%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%EF%BC%881%EF%BC%89-%E4%BD%95%E4%B8%BA%E5%B9%B6%E5%8F%91/" class="post-title-link" itemprop="url">多线程与并发编程(1)----何为并发</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2024-06-03 08:19:50" itemprop="dateCreated datePublished" datetime="2024-06-03T08:19:50+08:00">2024-06-03</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="计算机系统中的并发"><a href="#计算机系统中的并发" class="headerlink" title="计算机系统中的并发"></a>计算机系统中的并发</h1><p>多处理器:有多个处理器插座。</p>
<p>多核:一个处理器插座,只能有一个处理器安装,一个处理器有多个核心(独立执行计算的单元),每个核心可以有多个硬件线程(hart)</p>
<h2 id="何为并发?"><a href="#何为并发?" class="headerlink" title="何为并发?"></a>何为并发?</h2><p>并发:单个系统在同时执行多个独立的任务。</p>
<ul>
<li>硬件并发:一台计算机系统提供多个核心、单个CPU提供多个硬件线程(hart)。</li>
<li>软件并发:单个核心执行<strong>任务切换</strong>(task switch)。</li>
</ul>
<p>如果只有纯硬件并发,那么计算有多少硬件线程,就只能执行多少任务。</p>
<p>而软件并发,理论上可以在迷惑应用程序和用户的前提下,“同时”执行多个任务。</p>
<p>因此,现代计算机系统的并发是二者的结合。</p>
<p>硬件并发和软件并发还是有着许多不同:</p>
<ul>
<li>内存模型的假设</li>
<li>任务切换的负担</li>
</ul>
<h2 id="并发的方式(手段)"><a href="#并发的方式(手段)" class="headerlink" title="并发的方式(手段)"></a>并发的方式(手段)</h2><ul>
<li>多进程(每个进程只有一个执行线程)</li>
<li>多线程(每个进程有多个执行线程)</li>
</ul>
<h3 id="多进程并发"><a href="#多进程并发" class="headerlink" title="多进程并发"></a>多进程并发</h3><p>每个进程独立获得资源,进程之间互不干扰。</p>
<p>通过进程间通信手段进行通信</p>
<ul>
<li>信号</li>
<li>共享内存</li>
<li>管道</li>
<li>信号量</li>
<li>消息队列</li>
<li>等</li>
</ul>
<p>缺点:</p>
<ul>
<li>进程切换开销大,导致速度慢</li>
<li>进程管理需要操作系统维持(通过维持某些数据结构)</li>
</ul>
<p>优点:</p>
<ul>
<li>各个任务之间独立,安全性高。</li>
</ul>
<h3 id="多线程并发"><a href="#多线程并发" class="headerlink" title="多线程并发"></a>多线程并发</h3><p>在单个进程中执行多个线程。</p>
<ul>
<li>线程之间共享同一进程的大部分资源</li>
<li>但可以有自己的执行序列(PC寄存器),因此,可以同时独立完成多个任务。</li>
</ul>
<p>缺点:</p>
<ul>
<li>同一个进程的共享数据的访问,要被正确同步。</li>
</ul>
<p>多个单线程/进程间的通信(包含启动)要比单一进程中的多线程间的通信(包括启动)的开销大,若不考虑共享内存可能会带来的问题,多线程将会成为主流语言(包括C++)更青睐的并发途径。此外,C++标准并未对进程间通信提供任何原生支持,所以使用多进程的方式实现,这会依赖与平台相关的API。因此,本书只关注使用多线程的并发,并且在此之后所提到“并发”,均假设为多线程来实现。</p>
<h3 id="并发与并行"><a href="#并发与并行" class="headerlink" title="并发与并行"></a>并发与并行</h3><p>不同的关注点</p>
<p><strong>并行</strong>关注点:更注重性能,如何利用当前硬件的所有资源,来提高任务处理速度。</p>
<p><strong>并发</strong>关注点:任务分离,任务响应。</p>
<h3 id="关注点分离"><a href="#关注点分离" class="headerlink" title="关注点分离"></a>关注点分离</h3><p>互相独立的功能模块,可以在同一应用(进程)中分离,并行执行完成的任务,再进行信息的交换(需要手动处理交互逻辑)。</p>
<p>这种思想使得,线程的数量不再依赖CPU中可用(hart)的数量,因为对线程的划分是基于概念上的设计,而不是一种增加吞吐量的尝试(根本目的并不是为了能够处理更多任务,但这样做确实大部分情况下都能增加吞吐量。)。</p>
<h2 id="并发的方式"><a href="#并发的方式" class="headerlink" title="并发的方式"></a>并发的方式</h2><ul>
<li>指令级并行</li>
<li>数据级并行</li>
<li>线程级并性(任务并行)</li>
<li>多机并行</li>
</ul>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2024/06/02/%E4%BD%BF%E7%94%A8hexo%E6%90%AD%E5%BB%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="风">
<meta itemprop="description" content="笔记记录">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="wqllyx的网站">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2024/06/02/%E4%BD%BF%E7%94%A8hexo%E6%90%AD%E5%BB%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/" class="post-title-link" itemprop="url">使用hexo搭建自己的博客</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2024-06-02 20:04:18" itemprop="dateCreated datePublished" datetime="2024-06-02T20:04:18+08:00">2024-06-02</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2024-06-03 08:18:40" itemprop="dateModified" datetime="2024-06-03T08:18:40+08:00">2024-06-03</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="博客搭建的步骤"><a href="#博客搭建的步骤" class="headerlink" title="博客搭建的步骤"></a>博客搭建的步骤</h2><p>要搭建个人博客,首先要了解具体要实施哪些步骤,需要哪些工具,在了解了这些之后,再根据自己的需要,就可以很清晰的搭建自己的理想博客了。</p>
<h2 id="前置知识"><a href="#前置知识" class="headerlink" title="前置知识"></a>前置知识</h2><ul>
<li>静态网站和动态网站</li>
</ul>
<p>静态网站:内容如果不被发布者修改源代码后,重新编译,重新生成。网站上的资源永远不会改变。(比如,一篇博客文章,不与用户有任何数据交互。)</p>
<p>动态网站:底层与数据库交互,有一些放在数据库中的动态资源是可以随着你的请求而产生变化。(比如: 一个登录页面。需要与底层数据库进行数据交换。)</p>
<ul>
<li>网页生成器</li>
</ul>
<p>我们在自己的电脑上写完了自己文章之后,我们想要把他放到网上供别人在线查看的话,基本上用户都会使用基于web的浏览器在线浏览你的博客。然而浏览器都是基于html的web页面。对于md格式的文本文件,浏览器无法直接展示在他的界面上。(也不是无法展示,而是他会以纯文本文件的形式展示。),如果你直接把这些文件放到网上(比如一个公开服务器上或者托管平台上)供其他人访问,他们看到的只能是界面丑陋,晦涩生硬的纯文本。</p>
<p>而html就是用来渲染这些文本,让他们更生动(比如有颜色,有图片,格式多样)更方便用户阅读。所以,我们写的博客要是能够转换成web页面,然后再放在网上让别人阅读,那就好了。</p>
<p>我能想到的最笨的写博客的方法就是,你直接把你的内容嵌套到html文件里。这样也就不用任何第三方工具了。(但这既费力又需要很强的知识储备。没人愿意这样干。)</p>
<p>所以静态网站生成器就是干这个工作的:他会把你的md文件,自动嵌入到web页面中。不用你自己手动做这些操作,你要做的就是使用这些工具,遵循他们的格式要求就可以生成一个静态网站了。</p>
<ul>
<li>部署</li>
</ul>
<p>上面说的网页生成器所做的事情是转换,资源仍然在你的本地电脑上,我们如何将资源放到网上呢?这个基本常识就是(将资源放到大家可以访问的Ip地址上,也就是自己的网站了。)</p>
<p>能做这件事的有很多:</p>
<ul>
<li>自己购买一个云服务器,然后,将生成的静态资源直接放服务器的磁盘上,别人就可以通过IP地址访问你的资源。</li>
<li>通过一些托管平台(比如GitPage)本质上和服务器是一样的,只不过是别人帮你托管。</li>
<li>其他方式</li>
</ul>
<p>完成上面所有步骤,网站部署大致流程就完成了。剩下的就是一些细节问题(比如,如何写博客,如何使用生成器生成自己想要的网站样式(模板))等等问题。</p>
<h2 id="正式搭建"><a href="#正式搭建" class="headerlink" title="正式搭建"></a>正式搭建</h2><p>工具:使用md写博客,使用hexo生成网页,使用GItPage托管(部署)网站。</p>
<h3 id="hexo-的使用"><a href="#hexo-的使用" class="headerlink" title="hexo 的使用"></a>hexo 的使用</h3><p>在有自己博客的前提下,开始下载hexo,生成静态网页。</p>
<ul>
<li>安装:hexo是使用js写的平台,需要安装nodejs,并使用nodejs 包管理工具,NPM安装hexo软件。</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g hexo-cli</span><br></pre></td></tr></table></figure>
<ul>
<li>hexo是一个博客框架,首先需要生成一个初始博客模板(框架),命令为:</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hexo init blog_dir_name && <span class="built_in">cd</span> my-blog</span><br><span class="line"><span class="comment"># blog_dir_name 是你想要放置你的网站的根目录</span></span><br></pre></td></tr></table></figure>
<ul>
<li>这个框架就是hexo为你生成的一个静态网页模板,你可以在本地直接测试他的生成效果。你自己的网页基本也是这样。如果测试效果满意,就可以推送到服务器上,网站就建成了。本地测试命令(需要进入到博客根目录中)。</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">hexo server </span><br><span class="line"><span class="comment"># 可以简写为hexo s</span></span><br><span class="line">INFO Hexo is running at http://0.0.0.0:4000/. Press Ctrl+C to stop.</span><br></pre></td></tr></table></figure>
<p>这会在本地启动服务器,你可以通过浏览器访问相应的端口进行访问。</p>
<ul>
<li>创建你的第一篇文章(帖子)</li>
</ul>
<p>开始新帖子时,最好使用“草稿”功能。默认情况下,草稿不会发布,因此您可以自由更改其他帖子,同时保持未完成的草稿不公开。以下命令</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hexo new draft <span class="string">"My First Blog Post"</span></span><br><span class="line"><span class="comment"># creates -> ./source/_drafts/My-First-Blog-Post.md</span></span><br></pre></td></tr></table></figure>
<p>草稿帖子都会放在./source/_drafts/目录下,默认名是你的标题名。</p>
<blockquote>
<p>使用 hexo new 命令时,可以使用除“draft”之外的其他模板。查看 ./scaffolds/ 文件夹并阅读 Hexo <a target="_blank" rel="noopener" href="https://hexo.io/docs/writing.html#Scaffolds">文档</a>。</p>
</blockquote>
<ul>
<li>要编辑草稿,请导航至 ./source/_drafts/My-First-Blog-Post.md 并使用您最喜欢的 Markdown 编辑器打开该文件。<br> 让我们在您的新帖子中添加副标题和一些段落内容……</li>
</ul>
<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: My First Blog Post</span><br><span class="line"><span class="section">tags:</span></span><br><span class="line"><span class="section">---</span></span><br><span class="line"><span class="section">## Hello there</span></span><br><span class="line">This is some content.</span><br></pre></td></tr></table></figure>
<blockquote>
<p>位于 Markdown 文件顶部的破折号之间的内容称为“front-matter”。它是 Hexo 和活动主题使用的元数据。有关更多信息,请参阅 Front-Matter 上的 Hexo <a target="_blank" rel="noopener" href="https://hexo.io/docs/front-matter.html">文档</a>。</p>
</blockquote>
<p>保存对 Markdown 文件的更改将被正在运行的 hexo 服务器<strong>自动</strong>检测并重新生成为静态 HTML 文件,但您必须刷新浏览器才能查看更改。</p>
<p>如果您不喜欢每次都手动刷新浏览器,hexo-livereload 或 hexo-browsersync 插件可以自动执行此操作。</p>
<ul>
<li>安装 <code>hexo-browsersync</code> 插件(我个人最喜欢的):</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ npm install hexo-browsersync --save</span><br><span class="line">$ hexo server --draft --open <span class="comment"># 重启服务器</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>其他 Hexo 插件可以使用 npm 以相同的方式轻松安装。许多插件都具有可以在项目的 <code>_config.yml </code>文件中进行调整的配置。您需要查阅每个插件的文档以了解其特定的配置属性。对于 hexo-browsersync,默认设置可以正常工作,不需要编辑 _config.yml 文件。</p>
</blockquote>
<h3 id="一些写作技巧"><a href="#一些写作技巧" class="headerlink" title="一些写作技巧"></a>一些写作技巧</h3><ul>
<li>显示更少的内容</li>
</ul>
<p>假设您有一篇很长的文章,并且不喜欢整篇文章显示在列表页面中……<br>您可以使用 <code><!-- more --> </code>在 Markdown 中标记一个位置,以将其从列表页面中隐藏。它将被替换为“阅读更多”链接,该链接将打开文章的其余内容。</p>
<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: My First Blog Post</span><br><span class="line"><span class="section">tags:</span></span><br><span class="line"><span class="section">---</span></span><br><span class="line">This is a summary of the post.</span><br><span class="line"><!-- more --></span><br><span class="line"><span class="section">## Hello there</span></span><br><span class="line">This is some content.</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<ul>
<li>插入图像</li>
</ul>
<p>图像和其他资源文件可以放置在 ./source/ 文件夹下的<strong>子目录</strong>中。使用这张来自维基百科的原始 A-Team 图片作为测试。下载并保存到此路径:<br>./source/images/Ateam.jpg</p>
<p>编辑您的原始帖子,插入引用 /images/Ateam.jpg 的 Markdown 图像链接:</p>
<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: My First Blog Post</span><br><span class="line"><span class="section">tags:</span></span><br><span class="line"><span class="section">---</span></span><br><span class="line"><span class="section">## Hello there</span></span><br><span class="line">This is some content.</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>您应该在浏览器中看到类似这样的内容:</p>
<blockquote>
<p>Assets(就是你的图片什么的东西) can also be organized in <strong>folders for each post</strong>. It requires enabling the <code>post_asset_folder: true</code> setting in <code>_config.yml</code> and some knowledge of its relative path referencing behavior. See the <a target="_blank" rel="noopener" href="https://hexo.io/docs/asset-folders.html">Hexo documentation on Asset Folders</a> for more information.</p>
</blockquote>
<ul>
<li>发布草稿</li>
</ul>
<p>当需要将草稿移至“实时”帖子以供全世界查看时,请使用 hexo 发布命令:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo publish My-First-Blog-Post</span><br></pre></td></tr></table></figure>
<p>运行此命令时会发生一些事情:</p>
<ol>
<li><p>markdown 文件 My-First-Blog-Post.md 从 ./source/_drafts/ 移动到 ./source/_posts。</p>
</li>
<li><p>文件的“front-matter”更改为包含发布日期:</p>
</li>
</ol>
<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: My First Blog Post</span><br><span class="line">date: 2024-06-02 21:45:16 # <----</span><br><span class="line"><span class="section">tags:</span></span><br><span class="line"><span class="section">---</span></span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p>最后,准备整个站点以进行部署。运行<code>hexo generate</code>命令:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"> hexo generate</span><br><span class="line"># generates -> ./public/</span><br></pre></td></tr></table></figure>
<p>运行网站所需的所有内容都将放置在<code> ./public</code> 文件夹中。您已准备好将此文件夹传输到您的公共网络<strong>服务器</strong>或 CDN。</p>
<p>现在您已经了解了一般工作流程,接下来探索一些增强网站的其他方法:</p>
<ul>
<li>Commit your changes with <a target="_blank" rel="noopener" href="http://rogerdudler.github.io/git-guide/">Git</a> so that you can always revert back to a good state if things go wrong. Read <a target="_blank" rel="noopener" href="http://jr0cket.co.uk/hexo/managing-hexo-website-content-with-git-and-github.html">jr0cket’s guide for Managing your Hexo website with Git and Github</a>.</li>
<li>Customize your blog with various <a target="_blank" rel="noopener" href="https://hexo.io/docs/configuration.html">configuration settings</a> inside the <code>_config.yml</code> file.</li>
<li><a target="_blank" rel="noopener" href="https://hexo.io/docs/deployment.html">Automate your deployment</a> to GitHub Pages, Heroku, Amazon S3, and other providers via the <code>hexo deploy</code> command.</li>
<li>Enhance the style and functionality of your articles with built-in <a target="_blank" rel="noopener" href="https://hexo.io/docs/tag-plugins.html">tag plugins</a>.</li>
<li>Browse the growing catalog of <a target="_blank" rel="noopener" href="https://hexo.io/plugins/">community Hexo plugins</a>.</li>
<li>Reconfigure the <a target="_blank" rel="noopener" href="http://jr0cket.co.uk/hexo/hexo-theme-simple-changes.html">default Hexo “landscape” theme</a>, try out some of the <a target="_blank" rel="noopener" href="https://hexo.io/themes/">other free themes</a> that are available, or <a target="_blank" rel="noopener" href="https://hexo.io/docs/themes.html">create your own</a>.</li>
<li><a target="_blank" rel="noopener" href="https://hexo.io/docs/migration.html">Migrate your old posts</a> from another blog system (Wordpress, Jekyll, Octopress, etc.) to Hexo.</li>
</ul>
<p>And to see the options for a particular command, use <code>hexo help {command}</code>:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ hexo help new</span><br><span class="line">$ hexo help server</span><br></pre></td></tr></table></figure>
<hr>
<p>参考:</p>
<p><a target="_blank" rel="noopener" href="https://www.cgmartin.com/2016/01/03/getting-started-with-hexo-blog/">开始使用Hexo博客框架</a></p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<nav class="pagination">
<a class="extend prev" rel="prev" href="/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/">1</a><span class="page-number current">2</span>
</nav>
</div>
<script>
window.addEventListener('tabs:register', () => {
let { activeClass } = CONFIG.comments;
if (CONFIG.comments.storage) {
activeClass = localStorage.getItem('comments_active') || activeClass;
}
if (activeClass) {
let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
if (activeTab) {
activeTab.click();
}
}
});
if (CONFIG.comments.storage) {
window.addEventListener('tabs:click', event => {
if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
let commentClass = event.target.classList[1];
localStorage.setItem('comments_active', commentClass);
});
}
</script>
</div>
<div class="toggle sidebar-toggle">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
<aside class="sidebar">
<div class="sidebar-inner">
<ul class="sidebar-nav motion-element">
<li class="sidebar-nav-toc">
文章目录
</li>
<li class="sidebar-nav-overview">
站点概览
</li>
</ul>
<!--noindex-->
<div class="post-toc-wrap sidebar-panel">
</div>
<!--/noindex-->
<div class="site-overview-wrap sidebar-panel">
<div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
<p class="site-author-name" itemprop="name">风</p>
<div class="site-description" itemprop="description">笔记记录</div>
</div>
<div class="site-state-wrap motion-element">
<nav class="site-state">
<div class="site-state-item site-state-posts">
<a href="/archives/">
<span class="site-state-item-count">13</span>
<span class="site-state-item-name">日志</span>
</a>
</div>
<div class="site-state-item site-state-tags">
<span class="site-state-item-count">2</span>
<span class="site-state-item-name">标签</span>
</div>
</nav>
</div>
</div>
</div>
</aside>
<div id="sidebar-dimmer"></div>
</div>
</main>
<footer class="footer">
<div class="footer-inner">
<div class="copyright">
©
<span itemprop="copyrightYear">2024</span>
<span class="with-love">
<i class="fa fa-heart"></i>
</span>
<span class="author" itemprop="copyrightHolder">风</span>
</div>
<div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://muse.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
</div>
</div>
</footer>
</div>
<script src="/lib/anime.min.js"></script>
<script src="/lib/velocity/velocity.min.js"></script>
<script src="/lib/velocity/velocity.ui.min.js"></script>
<script src="/js/utils.js"></script>
<script src="/js/motion.js"></script>
<script src="/js/schemes/muse.js"></script>
<script src="/js/next-boot.js"></script>
</body>
</html>