D29:多線程

目錄

一. 多線程的概念(程序, 進(jìn)程, 線程)

二. 為什么使用多線程

三. 怎樣創(chuàng)建線程(2種方法)

四. 怎樣監(jiān)聽線程的結(jié)束, 取消線程

五. 線程的優(yōu)先級

六. 線程的同步鎖

  1. MyAccount類
  2. ViewController.m(此時執(zhí)行就會出現(xiàn)取款超出余額的問題, 需要添加同步鎖)
  3. 第一種添加線程鎖的方法(在MyAccount類中的- (void)withDraw:(float)tmpMoney方法中用@synchronized(self)添加鎖)
  4. 第二種添加線程鎖的方法(設(shè)一個成員變量:NSLock的對象)

七. NSOperation創(chuàng)建線程

  1. NSInvocationOperation
  2. NSBlockOperation
  3. 自定義NSBlockOperation的子類

八. Block

  1. 沒有返回值的block
  2. 有返回值, 返回值類型是基本類型(int為例)
  3. 有返回值, 返回值類型是基本類型(int為例)
  4. 測試

一. 多線程的概念

程序: 一段代碼, 是一個靜態(tài)的文件
進(jìn)程: 一個運(yùn)行起來的程序, 進(jìn)程實(shí)惠占用內(nèi)存的
線程: 進(jìn)程的組成部分, 一個進(jìn)程至少需要一個線程, 是進(jìn)程處理邏輯的基本單元, iOS程序運(yùn)行起來后, 默認(rèn)創(chuàng)建一個主線程, 自動維護(hù)這個主線程; 如果需要使用多線程, 必須自己手動創(chuàng)建和維護(hù)


二. 為什么使用多線程

將下載或者數(shù)據(jù)庫操作等放在了主線程里面, 會阻塞主線程, 造成一種假死的現(xiàn)象
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    UIButton *button1 = [MyUtility createButtonWithFrame:CGRectMake(100, 100, 80, 40) title:@"下載數(shù)據(jù)" backgroundImageName:nil target:self action:@selector(downloadAction)];
    [self.view addSubview:button1];
    
    // 按鈕2: 點(diǎn)擊打印一條信息
    UIButton *button2 = [MyUtility createButtonWithFrame:CGRectMake(100, 200, 80, 40) title:@"點(diǎn)擊" backgroundImageName:nil target:self action:@selector(clickButton)];
    [self.view addSubview:button2];
}

- (void)downloadAction
{
    NSLog(@"%s", __func__);
}

- (void)clickButton
{
    // 讓當(dāng)前的線程睡眠
    // 模擬下載數(shù)據(jù)需要的時間
    [NSThread sleepForTimeInterval:10];
    
    // 將下載放在了主線程里面, 會阻塞主線程
    // 造成了一種假死的現(xiàn)象
    
    // 類似下載或者數(shù)據(jù)庫操作的邏輯, 占用時間加長, 需要在主線程以外的線程中去處理
 
    NSLog(@"%s", __func__);
}

三. 怎樣創(chuàng)建線程

1. 使用NSThread來創(chuàng)建線程1
// 1. 點(diǎn)擊按鈕, 創(chuàng)建一個線程
UIButton *btn1 = [MyUtility createButtonWithFrame:CGRectMake(100, 100, 80, 40) title:@"創(chuàng)建線程1" backgroundImageName:nil target:self action:@selector(createThreadOne)];
[self.view addSubview:btn1];

// 創(chuàng)建線程1  
- (void)createThreadOne
{
    // detachNewThreadSelector:<#(SEL)#> toTarget:<#(id)#> withObject:<#(id)#>
    /*
     創(chuàng)建了一個線程, 同時將線程啟動
     第一個參數(shù): 線程的執(zhí)行體方法
     第二個參數(shù): 線程執(zhí)行體方法所屬的對象
     第三個參數(shù): 線程的執(zhí)行體方法的參數(shù)
     */
    NSNumber *n = @100;
    [NSThread detachNewThreadSelector:@selector(threadOne:) toTarget:self withObject:n];
}

// 線程1的執(zhí)行體方法
- (void)threadOne:(NSNumber *)n
{
    for (int i = 0; i < n.intValue; i++) {
        NSLog(@"線程1:%d", i);
        
        // 讓當(dāng)前線程睡眠
        [NSThread sleepForTimeInterval:5];
    }
}
2. 使用NSThread來創(chuàng)建線程2
// 2. 創(chuàng)建線程的第二種方式
UIButton *btn2 = [MyUtility createButtonWithFrame:CGRectMake(100, 200, 80, 40) title:@"創(chuàng)建線程2" backgroundImageName:nil target:self action:@selector(createThreadTwo)];
[self.view addSubview:btn2];
NSLog(@"%@", [NSThread currentThread].name);

- (void)createThreadTwo
{
    /*
     創(chuàng)建了一個線程, 線程沒有自動啟動
     第一個參數(shù): 線程執(zhí)行體方法所屬的對象
     第二個參數(shù): 線程的執(zhí)行體方法
     第三個參數(shù): 線程的執(zhí)行體方法的參數(shù)
     */
    NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(threadTwo) object:nil];
    // 設(shè)置線程的名字
    thread2.name = @"下載線程";
    // 手動啟動線程
    [thread2 start];
}

// 線程2的執(zhí)行體方法
- (void)threadTwo
{
    for (int i = 0; i < 100; i++) {
        // 獲取當(dāng)前的線程對象
        NSThread *currentThread = [NSThread currentThread];
        NSLog(@"%@ %d", currentThread.name, i+1);
    }
}

四. 怎樣監(jiān)聽線程的結(jié)束, 取消線程

1. 第1種取消線程方法
#import "ViewController.h"

@interface ViewController ()
{
    // 線程2是否取消
    BOOL _isThreadTwoCancel;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    // 創(chuàng)建2個線程, 并啟動
    // 第一個線程執(zhí)行for循環(huán), 第二個線程執(zhí)行死循環(huán)
    // 第一個線程結(jié)束后取消第二個線程
    // 程序接收到第二個線程結(jié)束的信息后, 停止第二個線程的執(zhí)行
    
    // 創(chuàng)建第一個線程
    [NSThread detachNewThreadSelector:@selector(threadOne) toTarget:self withObject:nil];
    
    // 創(chuàng)建第二個線程
    NSThread *t2 = [[NSThread alloc] initWithTarget:self selector:@selector(threadTwo) object:nil];
    [t2 start];
}

- (void)threadOne
{
    for (int i = 0; i < 1000; i++) {
        NSLog(@"執(zhí)行線程1");
        
        if (i == 999) {
            NSLog(@"線程1執(zhí)行完畢");
            // 取消線程2
            _isThreadTwoCancel = YES;
        }
    }
    
}

- (void)threadTwo
{
    
    int i = 0;

    while (true) {
        
        // 退出線程的方法需要放到循環(huán)里面
        if (_isThreadTwoCancel) {
            // 結(jié)束當(dāng)前的線程
            [NSThread exit];
        }
        NSLog(@"執(zhí)行線程2: %d", i+1);
        i++;
    }
}
2. 第2種取消線程方法
@interface ViewController ()
{
    …………………………………………………………………………………………
    
    NSThread *_t2;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    …………………………………………………………………………………………
            
    // 創(chuàng)建第二個線程
//    NSThread *t2 = [[NSThread alloc] initWithTarget:self selector:@selector(threadTwo) object:nil];
//    [t2 start];

    _t2 = [[NSThread alloc] initWithTarget:self selector:@selector(threadTwo) object:nil];
    [_t2 start];
}

- (void)threadOne
{
    for (int i = 0; i < 1000; i++) {
        NSLog(@"執(zhí)行線程1");
        
//        if (i == 999) {
//            NSLog(@"線程1執(zhí)行完畢");
//            // 取消線程2
//            _isThreadTwoCancel = YES;
//        }
        if (i == 999) {
            NSLog(@"線程1執(zhí)行完畢");
            // 取消線程2
            [_t2 cancel];
        }
    }
}

- (void)threadTwo
{
    
    int i = 0;

    while (true) {        
        if ([_t2 isCancelled]) {
            
            // 結(jié)束當(dāng)前的線程
            [NSThread exit];
        }
        
        NSLog(@"執(zhí)行線程2: %d", i+1);
        i++;
    }
}  
3. 通過通知中心監(jiān)聽線程的結(jié)束(給線程1和線程2先設(shè)置name屬性)
- (void)viewDidLoad {
    [super viewDidLoad];
    …………………………………………………………………………………………
            
    _t2.name = @"II";
    
    // 監(jiān)聽線程是否結(jié)束
    // NSThreadWillExitNotification
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadWillExit:) name:NSThreadWillExitNotification object:nil];
}

// 在線程結(jié)束的時候執(zhí)行一些操作
- (void)threadWillExit:(NSNotification *)n
{
    NSThread *t = [n object];
    NSLog(@"線程%@結(jié)束", t.name);
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)threadOne
{
    [NSThread currentThread].name = @"I";
    
    …………………………………………………………………………………………
}

五. 線程的優(yōu)先級

每個線程創(chuàng)建之后都有優(yōu)先級
優(yōu)先級在0-1之間, 值越大, 優(yōu)先級越高
默認(rèn)優(yōu)先級: 0.5
優(yōu)先級高的線程執(zhí)行的機(jī)會更大

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    // 每個線程創(chuàng)建之后都有優(yōu)先級
    // 優(yōu)先級在0-1之間, 值越大, 優(yōu)先級越高
    // 默認(rèn)優(yōu)先級: 0.5
    // 優(yōu)先級高的線程執(zhí)行的機(jī)會更大
    NSThread *t1 = [[NSThread alloc] initWithTarget:self selector:@selector(threadOne) object:nil];
    t1.name = @"線程I";
    [t1 start];
    t1.threadPriority = 0;
    
    NSThread *t2 = [[NSThread alloc] initWithTarget:self selector:@selector(threadTwo) object:nil];
    t2.name = @"線程II";
    [t2 start];
    t2.threadPriority = 1;
}

- (void)threadOne
{
    for (int i = 0; i < 1000; i++) {
        NSLog(@"執(zhí)行了%@: %d", [NSThread currentThread].name, i);
    }
}

- (void)threadTwo
{
    for (int i = 0; i < 1000; i++) {
        NSLog(@"執(zhí)行了%@: %d", [NSThread currentThread].name, i);
    }
}

六. 線程的同步鎖

模擬銀行賬戶取錢
創(chuàng)建一個賬戶對象, 模擬同時有2個人取錢

1. MyAccount類
#import <Foundation/Foundation.h>

@interface MyAccount : NSObject

/*
 accountNo: 賬戶的號碼
 money:     賬戶的余額
 */
- (instancetype)initWithAccountNo:(NSString *)accountNo money:(float)money;
- (void)withDraw:(float)tmpMoney;

@end  

#import "MyAccount.h"

@implementation MyAccount
{
    // 賬戶的號碼
    NSString *_accountNo;
    // 余額
    float _money;
}

- (instancetype)initWithAccountNo:(NSString *)accountNo money:(float)money
{
    self = [super init];
    if (self) {
        // 給成員變量賦值
        _accountNo = accountNo;
        _money = money;
    }
    return self;
}

// 取錢的操作
- (void)withDraw:(float)tmpMoney
{
    if (_money >= tmpMoney) {
        
        // 模擬取錢操作時需要一些時間
        [NSThread sleepForTimeInterval:0.01];
        
        // 取錢
        _money -= tmpMoney;
        
        NSLog(@"%@取了%f元錢", [NSThread currentThread].name, tmpMoney);
    } else {
        NSLog(@"余額不足");
    }
}

@end
2. ViewController.m(此時執(zhí)行就會出現(xiàn)取款超出余額的問題, 需要添加同步鎖)
#import "ViewController.h"
#import "MyAccount.h"

// 系統(tǒng)還有NSCondition 實(shí)現(xiàn)多線程的方式


@interface ViewController ()
{
    // 公共的賬戶
    MyAccount *_account;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    // 創(chuàng)建一個賬戶
    _account = [[MyAccount alloc] initWithAccountNo:@"Yuen" money:9000000];
    
    // 創(chuàng)建兩個線程
    NSThread *t1 = [[NSThread alloc] initWithTarget:self selector:@selector(threadOne) object:nil];
    [t1 start];
    
    NSThread *t2 = [[NSThread alloc] initWithTarget:self selector:@selector(threadTwo) object:nil];
    [t2 start];
}

- (void)threadOne
{
    [NSThread currentThread].name = @"Yank";
    [_account withDraw:8000000];
}

- (void)threadTwo
{
    [NSThread currentThread].name = @"Alfred";
    [_account withDraw:2000000];
}

@end  
3. 第一種添加線程鎖的方法(在MyAccount類中的- (void)withDraw:(float)tmpMoney方法中用@synchronized(self)添加鎖)
- (void)withDraw:(float)tmpMoney
{
    // @synchronized(self)添加鎖
    // 保證_money成員變量在同一時刻只有一個線程修改
    
    @synchronized(self) {
        
        if (_money >= tmpMoney) {
            
            // 模擬取錢操作時需要一些時間
            [NSThread sleepForTimeInterval:0.01];
            
            // 取錢
            _money -= tmpMoney;
            
            NSLog(@"%@取了%f元錢", [NSThread currentThread].name, tmpMoney);
        } else {
            NSLog(@"余額不足");
        }
    }
}
4. 第二種添加線程鎖的方法(設(shè)一個成員變量:NSLock的對象)
#import "MyAccount.h"

@implementation MyAccount
{
    …………………………………………………………………………………………
        
    // 線程鎖
    NSLock *_lock;
}

- (instancetype)initWithAccountNo:(NSString *)accountNo money:(float)money
{
    self = [super init];
    if (self) {
        …………………………………………………………………………………………        
        
        // 初始化線程鎖
        _lock = [[NSLock alloc] init];
    }
    return self;
}

// 取錢的操作
- (void)withDraw:(float)tmpMoney
{
    [_lock lock];
    
    …………………………………………………………………………………………
        
    // 釋放鎖
    [_lock unlock];
}

七. NSOperation創(chuàng)建線程

1. NSInvocationOperation
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    // 創(chuàng)建線程的隊(duì)列
    _queue = [[NSOperationQueue alloc] init];
    
    // 1. NSInvoationOperation
    /*
     第一個參數(shù): 線程的執(zhí)行體由哪個對象執(zhí)行
     第二個參數(shù): 線程的執(zhí)行體對象的方法
     第三個參數(shù): 線程的執(zhí)行體方法需要傳遞的實(shí)參
     */
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(threadOne) object:nil];
    
    // 線程執(zhí)行完成后調(diào)用的block
    [op1 setCompletionBlock:^{
        NSLog(@"線程I執(zhí)行完成");
    }];
    
    // 把線程添加到隊(duì)列中
    [_queue addOperation:op1];
}

- (void)threadOne
{
    for (int i = 0; i < 100; i++) {
        NSLog(@"線程一:%d", i);
    }

}

- (void)dealloc
{
    // 取消所有線程
    [_queue cancelAllOperations];
}
2. NSBlockOperation
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    // 創(chuàng)建線程的隊(duì)列
    _queue = [[NSOperationQueue alloc] init];
    
    // 2. NSBlockOperation
    // 參數(shù)是一個代碼塊, 是線程的執(zhí)行體
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 100; i++) {
            NSLog(@"執(zhí)行了線程II:%d", i);
        }
    }];
    
    // 線程執(zhí)行完成后調(diào)用的block
    [op1 setCompletionBlock:^{
        NSLog(@"線程II執(zhí)行完成");
    }];
    
    [_queue addOperation:op2];
}

- (void)dealloc
{
    // 取消所有線程
    [_queue cancelAllOperations];
}
3. 自定義NSBlockOperation的子類
  1. ImageOperation類

     #import <Foundation/Foundation.h>
     #import <UIKit/UIKit.h>
     
     @interface ImageOperation : NSOperation
     
     // 圖片的URL字符串
     @property (nonatomic, strong) NSString *urlString;
     // 圖片視圖對象
     @property (nonatomic, strong) UIImageView *imageView;
     
     @end
       
     #import "ImageOperation.h"
     
     @implementation ImageOperation
     
     // 自定義NSOperation類型, 需要實(shí)現(xiàn)main方法
     // 這個方法是線程的執(zhí)行體
     - (void)main
     {
         NSURL *url = [NSURL URLWithString:self.urlString];
         
         NSData *data = [NSData dataWithContentsOfURL:url];
         
         // 在主線程刷新UI
         [self performSelectorOnMainThread:@selector(refreshUI:) withObject:data waitUntilDone:YES];
     }
     
     - (void)refreshUI:(NSData *)data
     {
         self.imageView.image = [UIImage imageWithData:data];
     }
     
     @end  
    
  2. ViewController.m

     #import "ViewController.h"
     #import "ImageOperation.h"
     
     @interface ViewController ()
     {
         NSOperationQueue *_queue;
         UIImageView *_myImageView;
     }
     
     @end
     
     @implementation ViewController
     
     - (void)viewDidLoad {
         [super viewDidLoad];
         // Do any additional setup after loading the view, typically from a nib.
         
         // 創(chuàng)建線程的隊(duì)列
         _queue = [[NSOperationQueue alloc] init];
     
         _myImageView = [[UIImageView alloc] initWithFrame:CGRectMake(50, 100, 240, 320)];
         [self.view addSubview:_myImageView];
         
         // 第三種創(chuàng)建線程的方式
         // 創(chuàng)建一個線程, 下載一張圖片, 顯示到視圖上
         
         UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
         btn.frame = CGRectMake(100, 40, 80, 40);
         [btn setTitle:@"下載" forState:UIControlStateNormal];
         [self.view addSubview:btn];
         [btn addTarget:self action:@selector(downloadAction) forControlEvents:UIControlEventTouchUpInside];
     
     }
     
     - (void)downloadAction
     {
         // 創(chuàng)建線程下載圖片
         ImageOperation *op = [[ImageOperation alloc] init];
         // 圖片的URL
         op.urlString = @"http://img3.3lian.com/2006/027/08/007.jpg";
         // 圖片視圖
         op.imageView = _myImageView;
         
         // 添加到隊(duì)列里面
         [_queue addOperation:op];
     }
     
     @end
    

八. Block

1. 沒有返回值的block
- (void)testNoReturnBlock
{
    // 1. 沒有參數(shù)的
    // 聲明
    void (^block1)(void);
    // 賦值
    block1 = ^{
        NSLog(@"沒有返回值沒有參數(shù)");
    };
    // 使用
    block1();
    
    // 2. 有一個參數(shù), 參數(shù)是基本類型
    // 聲明
    void (^block2)(int a);
    // 賦值
    block2 = ^(int a){
        NSLog(@"a = %d", a);
    };
    // 使用
    block2(10);
    
    // 3. 有一個參數(shù), 參數(shù)是對象類型
    // 聲明
    void (^block3)(NSString *);
    // 賦值
    block3 = ^(NSString *str){
        NSLog(@"%@", str);
    };
    // 使用
    block3(@"張三");
    
    // 4. 兩個參數(shù), 一個基本類型, 一個對象類型
    // 聲明
    void (^block4)(int age, NSString *name);
    // 賦值
    block4 = ^(int age, NSString *name){
        NSLog(@"%@'s %d years old", name, age);
    };
    // 使用
    block4(54, @"Lau");
}
2. 有返回值, 返回值類型是基本類型(int為例)
- (void)testIntValueReturn
{
    // 1. 沒有參數(shù)
    // 聲明
    int (^block1)(void);
    // 賦值
    block1 = ^{
        return 10;
    };
    // 使用
    NSLog(@"%d", block1());
    
    // 2. 有一個參數(shù), 參數(shù)是基本類型
    // 聲明
    int (^block2)(int);
    // 賦值
    block2 = ^(int a){
        return a;
    };
    // 使用
    NSLog(@"%d", block2(5));
    
    // 3. 有一個參數(shù), 參數(shù)是對象類型
    // 聲明
    int (^block3)(NSString *);
    block3 = ^(NSString *str){
        return (int)str.length;
    };
    NSLog(@"%d", block3(@"Sunshine"));
    
    // 4. 有兩個參數(shù), 都是對象類型
    // 聲明
    int (^block4)(NSString *str1, NSString *str2);
    // 賦值
    block4 = ^(NSString *str1, NSString *str2){
        return (int)(str1.length + str2.length);
    };
    NSLog(@"%d", block4(@"Sunshine", @"Rain"));
}
3. 有返回值, 返回值類型是基本類型(int為例)
- (void)testNSStringValueReturn
{
    // 1. 沒有參數(shù)
    // 聲明
    NSString *(^block1)(void);
    // 賦值
    block1 = ^{
        return @"Nexus";
    };
    // 使用
    NSLog(@"%@",block1());
    
    // 2. 有一個參數(shù), 參數(shù)是基本類型
    // 聲明
    NSString *(^block2)(int);
    // 賦值
    block2 = ^(int a){
        return [NSString stringWithFormat:@"%d", a];
    };
    // 使用
    NSLog(@"%@", block2(5));
    
    // 3. 有一個參數(shù), 參數(shù)是對象類型
    // 聲明
    NSString *(^block3)(NSString *);
    block3 = ^(NSString *str){
        return str;
    };
    NSLog(@"%@", block3(@"Sunshine"));
    
    // 4. 有兩個參數(shù), 都是對象類型
    // 聲明
    NSString *(^block4)(NSString *str1, NSString *str2);
    // 賦值
    block4 = ^(NSString *str1, NSString *str2){
        return [NSString stringWithFormat:@"%@ %@", str1, str2];
    };
    NSLog(@"%@", block4(@"Sunshine", @"Rain"));
}  
4. 測試
- (void)test
{
    // 1. 沒有返回值, 有2個參數(shù), 一個基本類型, 一個對象類型
    // 聲明并賦值
    void (^block1)(int age, NSString *name) = ^(int age, NSString *name){
        NSLog(@"%@'s %d years old", name, age);
    };
    // 使用
    block1(23, @"Yuen");
    
    // 2. 返回值為int類型, 有2個參數(shù), 參數(shù)是NSString類型
    // 聲明并賦值
    int (^block2)(NSString *, NSString *) = ^(NSString *str1, NSString *str2){
        return (int)(str1.length + str2.length);
    };
    // 使用
    NSLog(@"%d", block2(@"Sunshine", @"Rain"));
    
    // 3. 返回值是NSString類型, 有2個參數(shù), 參數(shù)是NSString類型
    NSString *(^block3)(NSString *,NSString *) = ^(NSString *str1, NSString *str2){
        return [NSString stringWithFormat:@"%@%@", str1, str2];
    };
    // 使用
    NSLog(@"%@", block3(@"Sunshine", @"Rain"));
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市仓手,隨后出現(xiàn)的幾起案子欲芹,更是在濱河造成了極大的恐慌鸣峭,老刑警劉巖剩檀,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桨嫁,死亡現(xiàn)場離奇詭異蔚润,居然都是意外死亡训貌,警方通過查閱死者的電腦和手機(jī)灌危,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門康二,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人勇蝙,你說我怎么就攤上這事沫勿。” “怎么了味混?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵产雹,是天一觀的道長。 經(jīng)常有香客問我翁锡,道長蔓挖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任馆衔,我火速辦了婚禮瘟判,結(jié)果婚禮上怨绣,老公的妹妹穿的比我還像新娘。我一直安慰自己拷获,他們只是感情好梨熙,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刀诬,像睡著了一般咽扇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陕壹,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天质欲,我揣著相機(jī)與錄音,去河邊找鬼糠馆。 笑死嘶伟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的又碌。 我是一名探鬼主播九昧,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼毕匀!你這毒婦竟也來了铸鹰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤皂岔,失蹤者是張志新(化名)和其女友劉穎蹋笼,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躁垛,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剖毯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了教馆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逊谋。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖土铺,靈堂內(nèi)的尸體忽然破棺而出胶滋,到底是詐尸還是另有隱情,我是刑警寧澤舒憾,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布镀钓,位于F島的核電站,受9級特大地震影響镀迂,放射性物質(zhì)發(fā)生泄漏丁溅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一探遵、第九天 我趴在偏房一處隱蔽的房頂上張望窟赏。 院中可真熱鬧妓柜,春花似錦、人聲如沸涯穷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拷况。三九已至作煌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赚瘦,已是汗流浹背粟誓。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留起意,地道東北人鹰服。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像揽咕,于是被迫代替她去往敵國和親悲酷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容