C#专题之线程

5.1 资源访问冲突问题


internal class StateObject
{
    private int state = 5;
    public void ChangeState()
    {
        if (state == 5)
        {
            state++;
            Console.WriteLine("state: " + state + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
        }
        state = 5;
    }
}
static void Main(string[] args)
{
    StateObject state = new StateObject();

    for (int i = 0; i < 100; i++)
    {
        Thread t = new Thread(state.ChangeState);
        t.Start();
    }
}

解决——加锁

internal class StateObject
{
    private Object _lock = new Object();

    private int state = 5;
    public void ChangeState()
    {
        lock (_lock)
        {
            if (state == 5)
            {
                state++;
                Console.WriteLine("state: " + state + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
            }
            state = 5;
        }
    }
}

异步委托的方式启动线程


static void Test()
{
    Console.WriteLine("Test Started");
    Console.WriteLine("Test Running");
    Thread.Sleep(3000);
    Console.WriteLine("Test Completed");
}

delegate void TestDelegate();

static void Main(string[] args)
{
    TestDelegate testDelegate = Test;
    testDelegate.BeginInvoke(null, null);
    Console.WtiteLine("Main Completed");
}

线程的优先级和线程的状态


线程的优先级

在 Thread 类中,可以设置 Priority​ 属性,以影响线程的基本优先级,Priority​ 属性是 ThreadPriority​ 枚举定义的一个值。定义的级别有:Highest​、AboveNormal​、Normal​、BelowNormal​ 和 Lowest​。

线程的状态

  • 获取线程的状态(Running 还是 Unstarted…),当我们通过调用 Thread 对象的 Start() 方法,可以创建线程,但是调用了 Start()​ 方法之后,新线程不是马上进入 Running 状态,而是处于 Unstarted 状态,只有当操作系统的线程调度器选择了要运行的线程,这个线程的状态才会修改为 Running 状态。我们使用 Thread.Sleep()​ 方法可以让当前线程休眠进入 WaitSleepJoin 状态。
  • 使用 Thread对象的 Abort()​ 方法可以停止线程。调用这个方法,会在要终止的线程中抛出一个 ThreadAbortException​ 类型的异常,我们可以 try catch 这个异常,然后在线程终止前做一些清理的工作。
  • 如果需要等待线程的结束,可以调用 Thread 对象的 Join()​ 方法,表示把 Thread 加入进来,暂停当前线程,并把它设置为 WaitSleepJoin 状态,直到加入的线程完成为止。

线程池


static void Main(string[] args)
{
    for (int i = 0; i < 10; i++)
    {
        ThreadPool.QueueUserWorkItem(Download);
    }
    Thrad.Sleep(500);
}

static void Download(Object state)
{
    for (int i = 0; i < 3; i++)
    {
        Console.WriteLine("Downloading...: " + Thread.CurrentThread.ManageThreadId);
        Thread.Sleep(100);
    }
}
  • 使用线程池启动的线程默认是后台线程
  • 如果进程的所有前台线程都结束了,所有的后台线程就会停止,不能把入池的线程改为前台线程。
  • 不能给入池的线程设置优先级或名称
  • 入池的线程只能用于时间较短的任务,如果线程要一直运行,就应该使用 Thread​ 类创建一个线程。

死锁问题


internal class StateObject
{
    private Object _lock1 = new Object();
    private Object _lock2 = new Object();
  
    private int state1 = 5;
    private int state2 = 5;
  
    public void ChangeState()
    {
        Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第1把锁");
        lock (_lock1)
        {
             lock (_lock2)
            {
                Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第2把锁");
                if (state1 == 5)
                {
                    state1++;
                    Console.WriteLine("state: " + state1 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
                }
                state1 = 5;
  
                if (state2 == 5)
                {
                    state2++;
                    Console.WriteLine("state: " + state2 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
                }
                state2 = 5;
            }
        }
    }

    public void ChangeState()
    {
        lock (_lock2)
        {
            Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第2把锁");
             lock (_lock1)
            {
                Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第1把锁");
                if (state1 == 5)
                {
                    state1++;
                    Console.WriteLine("state: " + state1 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
                }
                state1 = 5;
  
                if (state2 == 5)
                {
                    state2++;
                    Console.WriteLine("state: " + state2 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
                }
                state2 = 5;
            }
        }
    }
}

解决方法——规定相同的拿锁顺序

internal class StateObject
{
    private Object _lock1 = new Object();
    private Object _lock2 = new Object();
  
    private int state1 = 5;
    private int state2 = 5;
  
    public void ChangeState()
    {
        Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第1把锁");
        lock (_lock1)
        {
             lock (_lock2)
            {
                Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第2把锁");
                if (state1 == 5)
                {
                    state1++;
                    Console.WriteLine("state: " + state1 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
                }
                state1 = 5;
  
                if (state2 == 5)
                {
                    state2++;
                    Console.WriteLine("state: " + state2 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
                }
                state2 = 5;
            }
        }
    }

    public void ChangeState()
    {
        lock (_lock1)
        {
            Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第1把锁");
             lock (_lock2)
            {
                Console.WriteLine(Thread.CurrentThread.ManageThreadId + "拿到了第2把锁");
                if (state1 == 5)
                {
                    state1++;
                    Console.WriteLine("state: " + state1 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
                }
                state1 = 5;
  
                if (state2 == 5)
                {
                    state2++;
                    Console.WriteLine("state: " + state2 + " 线程ID:" + Thread.CurrentThread.ManageThreadId);
                }
                state2 = 5;
            }
        }
    }
}

使用Thread启动线程和传输数据


启动线程

static void Test()
{
    Console.WriteLine("Test Started");
    Console.WriteLine("Test Running");
    Thread.Sleep(1000);
    Console.WriteLine("Test Completed");
}

static void Main(string[] args)
{
    Thread t = new Thread(Test);
    t.Start();
    Console.WriteLine("Main Completed");
}

使用 Lambda 表达式:

static void Main(string[] args)
{
    Thread t = new Thread(() => Console.WriteLine("Child Thread: " + Thread.CurrentThread.ManageThreadId));
    t.Start();
    Console.WriteLine("Main Completed: " + Thread.CurrentThread.ManageThreadId);
}

使用匿名方法:

static void Main(string[] args)
{
    Thread t = new Thread(delegate () {
        Console.WriteLine("Child Thread: " + Thread.CurrentThread.ManageThreadId)
    });
    t.Start();
    Console.WriteLine("Main Completed: " + Thread.CurrentThread.ManageThreadId);
}

传递数据

static void Download(Object x)
{
    string str = o as string;
    Console.WriteLine(str);
}

static void Main(string[] args)
{
    Thread t = new Thread(Download);
    t.Start("http://www.xxx.com");
}

Start()​ 方法只能接收无参的方法或只有一个参数的方法

传递多个数据

public struct Data
{
    public string message;
    public int age;
}

static void Download(Object x)
{
    Data data = (Data)o;
    Console.WriteLine(data.message);
    Console.WriteLine(data.age);
}

static void Main(string[] args)
{
    Data data = new Data();
    data.message = "";
    data.age = 12;  

    Thread t = new Thread(Download);
    t.Start(data);
}

任务


static void Main(string[] args)
{
    TaskFactory tf = new TaskFactory();
    Task t = tf.StartNew(Test);
    Thread.Sleep(5000);
}

static void Test()
{
    for (int i = 0; i < 10000; i++)
    {
        Console.WriteLine("A");
    }
}
static void Main(string[] args)
{
    Task t = new Task(Test);
    t.Start();
    Thread.Sleep(5000);
}

static void Test()
{
    for (int i = 0; i < 10000; i++)
    {
        Console.WriteLine("A");
    }
}

连续任务

如果一个任务t1的执行是依赖于另一个任务t2的,那么就需要在这个任务t2执行完毕后才开始执行t1。这个时候我们就可以使用连续任务。

static void FirstDownload()
{
    Console.WriteLine("Downloading ...");
    Thread.Sleep(2000);
}

static void SecondAlert(Task t)
{
    Console.WriteLine("下载完成!");
}

static void Main(string[] args)
{
    Task t1 = new Task(FirstDownload);
    t1.ContinueWith(SecondAlert);
    t1.Start();
    Thread.Sleep(5000);
}
static void FirstDownload()
{
    Console.WriteLine("Downloading ...");
    Thread.Sleep(2000);
}

static void SecondAlert(Task t)
{
    Console.WriteLine("下载完成!");
}

static void Main(string[] args)
{
    Task t1 = new Task(FirstDownload);
    Task t2 = t1.ContinueWith(SecondAlert);
    Task t3 = t2.ContinueWith(SecondAlert);
    t1.Start();
    Thread.Sleep(5000);
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值