C#线程同步方法汇总

从0自学C#12--线程同步解决方法汇总以及优缺点 线程同步解决方法汇总以及优缺点,锁机制和各种锁比较:volatile、Interlocked类(互锁)、spinlock(自旋锁)、Mutex、Moniter、Lock、ReaderWriterLock、双检锁等。 阅读详情

归纳一下:C#线程同步的几种方法

  我们在编程的时候,有时会使用多线程来解决问题,比如你的程序需要在 后台处理一大堆数据,但还要使用户界面处于可操作状态;或者你的程序需要访问一些外部资源如数据库或网络文件等。这些情况你都可以创建一个子线程去处理, 然而,多线程不可避免地会带来一个问题,就是线程同步的问题。如果这个问题处理不好,我们就会得到一些非预期的结果。

  在网上也看过一些关于线程同步的文章,其实线程同步有好几种方法,下面我就简单的做一下归纳。

  一、volatile关键字

  volatile是最简单的一种同步方法,当然简单是要付出代价的。它只能在变量一级做同步,volatile的含义就是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我。(【转自 www.bitsCN.com 】)因此,当多线程同时访问该变量时,都将直接操作主存,从本质上做到了变量共享。

  能够被标识为volatile的必须是以下几种类型:(摘自MSDN)

  • Any reference type.
  • Any pointer type (in an unsafe context).
  • The types sbyte, byte, short, ushort, int, uint, char, float, bool.
  • An enum type with an enum base type of byte, sbyte, short, ushort, int, or uint.

      如:

      Code 
    public  class  A
    {
    private  volatile  int  _i;
    public  int  I
    {
    get  {  return  _i; }
    set  { _i  =  value; }
    }
    }

      但volatile并不能实现真正的同步,因为它的操作级别只停留在变量级别,而不是原子级别。如果是在单处理器系统中,是没有任何问题的,变量在主存中没有机会被其他人修改,因为只有一个处理器,这就叫作processor Self-Consistency。但在多处理器系统中,可能就会有问题。 每个处理器都有自己的data cach,而且被更新的数据也不一定会立即写回到主存。所以可能会造成不同步,但这种情况很难发生,因为cach的读写速度相当快,flush的频率也相当高,只有在压力测试的时候才有可能发生,而且几率非常非常小。

      二、lock关键字

      lock是一种比较好用的简单的线程同步方式,它是通过为给定对象获取互斥锁来实现同步的。它可以保证当一个线程在关键代码段的时候,另一个线程不会进来,它只能等待,等到那个线程对象被释放,也就是说线程出了临界区。用法:

      Code 
    public  void  Function() 
    {
    object  lockThis  =  new  object  (); 
    lock  (lockThis)
    {
    //  Access thread-sensitive resources. 
    }
    }

     

     

      lock的参数必须是基于引用类型的对象,不要是基本类型像 bool,int什么的,这样根本不能同步,原因是lock的参数要求是对象,如果传入int,势必要发生装箱操作,这样每次lock的都将是一个新的不 同的对象。最好避免使用public类型或不受程序控制的对象实例,因为这样很可能导致死锁。特别是不要使用字符串作为lock的参数,因为字符串被 CLR“暂留”,就是说整个应用程序中给定的字符串都只有一个实例,因此更容易造成死锁现象。建议使用不被“暂留”的私有或受保护成员作为参数。其实某些 类已经提供了专门用于被锁的成员,比如Array类型提供SyncRoot,许多其它集合类型也都提供了SyncRoot。

      所以,使用lock应该注意以下几点: 

      1、如果一个类的实例是public的,最好不要lock(this)。因为使用你的类的人也许不知道你用了lock,如果他new了一个实例,并且对这个实例上锁,就很容易造成死锁。

      2、如果MyType是public的,不要lock(typeof(MyType))

      3、永远也不要lock一个字符串

      三、System.Threading.Interlocked

      对于整数数据类型的简单操作,可以用 Interlocked 类的成员来实现线程同步,存在于System.Threading命名空间。Interlocked类有以下方法:Increment ,Decrement , Exchange 和CompareExchange 。使用Increment 和Decrement 可以保证对一个整数的加减为一个原子操作。Exchange 方法自动交换指定变量的值。CompareExchange 方法组合了两个操作:比较两个值以及根据比较的结果将第三个值存储在其中一个变量中。比较和交换操作也是按原子操作执行的。如:

      Code 
    int  i  =  0  ;
    System.Threading.Interlocked.Increment(
      ref  i);
    Console.WriteLine(i);
    System.Threading.Interlocked.Decrement(
      ref  i);
    Console.WriteLine(i);
    System.Threading.Interlocked.Exchange(
      ref  i,  100  );
    Console.WriteLine(i);
    System.Threading.Interlocked.CompareExchange(
      ref  i,  10  ,  100  );

    Output:

      四、Monitor

      Monitor类提供了与lock类似的功能,不过与lock不同的是,它 能更好的控制同步块,当调用了Monitor的Enter(Object o)方法时,会获取o的独占权,直到调用Exit(Object o)方法时,才会释放对o的独占权,可以多次调用Enter(Object o)方法,只需要调用同样次数的Exit(Object o)方法即可,Monitor类同时提供了TryEnter(Object o,[int])的一个重载方法,该方法尝试获取o对象的独占权,当获取独占权失败时,将返回false。

      但使用 lock 通常比直接使用 Monitor 更可取,一方面是因为 lock 更简洁,另一方面是因为 lock 确保了即使受保护的代码引发异常,也可以释放基础监视器。这是通过 finally 中调用Exit来实现的。事实上,lock 就是用 Monitor 类来实现的。下面两段代码是等效的:

     Code 
    lock 
    (x)
    {
    DoSomething();
    }
     
    等效于

    object obj = ( object 
    )x;
    System.Threading.Monitor.Enter(obj);
    try 

    {
    DoSomething();
    }
    finally 
    {
    System.Threading.Monitor.Exit(obj);
    }

     

    关于用法,请参考下面的代码:

     Code 
    private static object m_monitorObject = new object 
    ();
    [STAThread]
    static void Main( string 
    [] args)
    {
    Thread thread 
    = new Thread( new 
    ThreadStart(Do));
    thread.Name 
    = " Thread1 " 
    ;
    Thread thread2 
    = new Thread( new 
    ThreadStart(Do));
    thread2.Name 
    = " Thread2 " 
    ;
    thread.Start();
    thread2.Start();
    thread.Join();
    thread2.Join();
    Console.Read();
    }
    static void 
    Do()
    {
    if ( ! 
    Monitor.TryEnter(m_monitorObject))
    {
    Console.WriteLine(
     " Can't visit Object " + 
    Thread.CurrentThread.Name);
    return 
    ;
    }
    try 

    {
    Monitor.Enter(m_monitorObject);
    Console.WriteLine(
     
    " Enter Monitor " +  Thread.CurrentThread.Name);
    Thread.Sleep(
     5000 
    );
    }
    finally 

    {
    Monitor.Exit(m_monitorObject);
    }
    }

      当线程1获取了m_monitorObject对象独占权时,线程2尝试调用TryEnter(m_monitorObject),此时会由于无法获取独占权而返回false,输出信息如下:

      另外,Monitor还提供了三个静态方法 Monitor.Pulse(Object o),Monitor.PulseAll(Object o)和Monitor.Wait(Object o ) ,用来实现一种唤醒机制的同步。关于这三个方法的用法,可以参考MSDN,这里就不详述了。

      五、Mutex

      在使用上,Mutex与上述的Monitor比较接近,不过Mutex不具 备Wait,Pulse,PulseAll的功能,因此,我们不能使用Mutex实现类似的唤醒的功能。不过Mutex有一个比较大的特点,Mutex是 跨进程的,因此我们可以在同一台机器甚至远程的机器上的多个进程上使用同一个互斥体。尽管Mutex也可以实现进程内的线程同步,而且功能也更强大,但这 种情况下,还是推荐使用Monitor,因为Mutex类是win32封装的,所以它所需要的互操作转换更耗资源。

      六、ReaderWriterLock

      在考虑资源访问的时候,惯性上我们会对资源实施lock机制,但是在某些情 况下,我们仅仅需要读取资源的数据,而不是修改资源的数据,在这种情况下获取资源的独占权无疑会影响运行效率,因此.Net提供了一种机制,使用 ReaderWriterLock进行资源访问时,如果在某一时刻资源并没有获取写的独占权,那么可以获得多个读的访问权,单个写入的独占权,如果某一时 刻已经获取了写入的独占权,那么其它读取的访问权必须进行等待,参考以下代码:

     Code 
    private static ReaderWriterLock m_readerWriterLock = new 
    ReaderWriterLock();
    private static int m_int = 0 
    ;
    [STAThread]
    static void Main( string 
    [] args)
    {
    Thread readThread 
    = new Thread( new 
    ThreadStart(Read));
    readThread.Name 
    = " ReadThread1 " 
    ;
    Thread readThread2 
    = new Thread( new 
    ThreadStart(Read));
    readThread2.Name 
    = " ReadThread2 " 
    ;
    Thread writeThread 
    = new Thread( new 
    ThreadStart(Writer));
    writeThread.Name 
    = " WriterThread " 
    ;
    readThread.Start();
    readThread2.Start();
    writeThread.Start();
    readThread.Join();
    readThread2.Join();
    writeThread.Join();

    Console.ReadLine(); 
    }
    private static void 
    Read()
    {
    while ( true 
    )
    {
    Console.WriteLine(
     " ThreadName " + Thread.CurrentThread.Name + " AcquireReaderLock " 
    );
    m_readerWriterLock.AcquireReaderLock(
     10000 
    );
    Console.WriteLine(String.Format(
     " ThreadName : {0} m_int : {1} " 
    , Thread.CurrentThread.Name, m_int));
    m_readerWriterLock.ReleaseReaderLock();
    }
    }

    private static void 
    Writer()
    {
    while ( true 
    )
    {
    Console.WriteLine(
     " ThreadName " + Thread.CurrentThread.Name + " AcquireWriterLock " 
    );
    m_readerWriterLock.AcquireWriterLock(
     1000 
    );
    Interlocked.Increment(
     ref 
    m_int);
    Thread.Sleep(
     5000 
    );
    m_readerWriterLock.ReleaseWriterLock();
    Console.WriteLine(
     " ThreadName " + Thread.CurrentThread.Name + " ReleaseWriterLock " 
    );
    }
    }

    在程序中,我们启动两个线程获取m_int的读取访问权,使用一个线程获取m_int的写入独占权,执行代码后,输出如下:

    可以看到,当WriterThread获取到写入独占权后,任何其它读取的线程 都必须等待,直到WriterThread释放掉写入独占权后,才能获取到数据的访问权,应该注意的是,上述打印信息很明显显示出,可以多个线程同时获取 数据的读取权,这从ReadThread1和ReadThread2的信息交互输出可以看出。

      七、SynchronizationAttribute

      当我们确定某个类的实例在同一时刻只能被一个线程访问时,我们可以直接将类标识成Synchronization的,这样,CLR会自动对这个类实施同步机制,实际上,这里面涉及到同步域的概念,当类按如下设计时,我们可以确保类的实例无法被多个线程同时访问
      1). 在类的声明中,添加System.Runtime.Remoting.Contexts.SynchronizationAttribute属性。
        2). 继承至System.ContextBoundObject
        需要注意的是,要实现上述机制,类必须继承至System.ContextBoundObject,换句话说,类必须是上下文绑定的。
        一个示范类代码如下:

     Code 
    [System.Runtime.Remoting.Contexts.Synchronization]
    public class 
    SynchronizedClass : System.ContextBoundObject
    {

    }

     

      八、MethodImplAttribute

      如果临界区是跨越整个方法的,也就是说,整个方法内部的代码都需要上锁的 话,使用MethodImplAttribute属性会更简单一些。这样就不用在方法内部加锁了,只需要在方法上面加上 [MethodImpl(MethodImplOptions.Synchronized)] 就可以了,MehthodImpl和MethodImplOptions都在命名空间System.Runtime.CompilerServices 里面。但要注意这个属性会使整个方法加锁,直到方法返回,才释放锁。因此,使用上不太灵活。如果要提前释放锁,则应该使用Monitor或lock。我们来看一个例子:

     Code 
    [MethodImpl(MethodImplOptions.Synchronized)]
    public void 
    DoSomeWorkSync()
    {
    Console.WriteLine(
     " DoSomeWorkSync() -- Lock held by Thread " + 

    Thread.CurrentThread.GetHashCode());
    Thread.Sleep(
     
    1000  );
    Console.WriteLine(
     " DoSomeWorkSync() -- Lock released by Thread " + 

    Thread.CurrentThread.GetHashCode());
    }
    public void  DoSomeWorkNoSync()
    {
    Console.WriteLine(
     " DoSomeWorkNoSync() -- Entered Thread is " + 

    Thread.CurrentThread.GetHashCode());
    Thread.Sleep(
     
    1000  );
    Console.WriteLine(
     " DoSomeWorkNoSync() -- Leaving Thread is " + 

    Thread.CurrentThread.GetHashCode());
    }

    [STAThread]
    static void Main( string  [] args)
    {
    MethodImplAttr testObj 
    = new 
    MethodImplAttr();
    Thread t1 
    = new Thread( new 
    ThreadStart(testObj.DoSomeWorkNoSync));
    Thread t2 
    = new Thread( new 
    ThreadStart(testObj.DoSomeWorkNoSync));
    t1.Start();
    t2.Start();
    Thread t3 
    = new Thread( new 
    ThreadStart(testObj.DoSomeWorkSync));
    Thread t4 
    = new Thread( new 
    ThreadStart(testObj.DoSomeWorkSync));
    t3.Start();
    t4.Start();

    Console.ReadLine(); 
    }

    这里,我们有两个方法,我们可以对比一下,一个是加了属性MethodImpl 的DoSomeWorkSync(),一个是没加的DoSomeWorkNoSync()。在方法中Sleep(1000)是为了在第一个线程还在方法中 时,第二个线程能够有足够的时间进来。对每个方法分别起了两个线程,我们先来看一下结果:

    可以看出,对于线程1和2,也就是调用没有加属性的方法的线程,当线程2进入方法后,还没有离开,线程1有进来了,这就是说,方法没有同步。我们再来看看线程3和4,当线程3进来后,方法被锁,直到线程3释放了锁以后,线程4才进来。

      九、同步事件和等待句柄

      用lock和Monitor可以很好地起到线程同步的作用,但它们无法实现线程之间传递事件。如果要实现线程同步的同时,线程之间还要有交互,就要用到同步事件。同步事件是有两个状态(终止和非终止)的对象,它可以用来激活和挂起线程。

      同步事件有两种:AutoResetEvent 和 ManualResetEvent 。 它们之间唯一不同的地方就是在激活线程之后,状态是否自动由终止变为非终止。AutoResetEvent自动变为非终止,就是说一个 AutoResetEvent只能激活一个线程。而ManualResetEvent要等到它的Reset方法被调用,状态才变为非终止,在这之 前,ManualResetEvent可以激活任意多个线程。

      可以调用WaitOne、WaitAny或WaitAll来使线程等待事件。它们之间的区别可以查看MSDN。当调用事件的 Set 方法时,事件将变为终止状态,等待的线程被唤醒。

      来看一个例子,这个例子是MSDN上的。因为事件只用于一个线程的激活,所以使用 AutoResetEvent 或 ManualResetEvent 类都可以。

     Code 
    static 
    AutoResetEvent autoEvent;

    static void 
    DoWork()
    {
    Console.WriteLine(
     " worker thread started, now waiting on event " 
    );
    autoEvent.WaitOne();
    Console.WriteLine(
     " worker thread reactivated, now exiting " 
    );
    }

    [STAThread]
    static void Main( string 
    [] args)
    {
    autoEvent 
    = new AutoResetEvent( false 
    );

    Console.WriteLine(
     " main thread starting worker thread " 
    );
    Thread t 
    = new Thread( new 
    ThreadStart(DoWork));
    t.Start();

    Console.WriteLine(
     " main thrad sleeping for 1 second " 
    );
    Thread.Sleep(
     1000 
    );

    Console.WriteLine(
     " main thread signaling worker thread " 
    );
    autoEvent.Set();

    Console.ReadLine(); 
    }

    我们先来看一下输出:

    在主函数中,首先创建一个AutoResetEvent的实例,参数false 表示初始状态为非终止,如果是true的话,初始状态则为终止。然后创建并启动一个子线程,在子线程中,通过调用AutoResetEvent的 WaitOne方法,使子线程等待指定事件的发生。然后主线程等待一秒后,调用AutoResetEvent的Set方法,使状态由非终止变为终止,重新 激活子线程。

     

C# 线程同步的几种方法 互斥锁是一个互斥的同步对象,意味着同一时间有且仅有一个线程可以获取它,互斥锁可适用于一个共享资源每次只能被一个线程访问的情况,如果线程获取互斥体,则需要获取该互斥体的第二个线程将挂起,直到第一个线程释放该互斥体。如上图所示,有四个任务,每个任务里面做各自的事情,当有一个屏障发出信号后就会开始等待,等待到4个全部到达,就会解除等待,同时进行下一步。自旋锁是指当一个线程在获取锁对象的时候,如果锁已经被其它线程获取,那么这个线程将会循环等待,不断的去获取锁,直到获取到了锁。否则,返回false。 阅读详情

相关推荐

C# 浅谈线程同步Lock、Monitor、Interlocked、Mutex等多种线程锁及测试汇总

文章目录一、前言二、线程锁的类型1. volatile关键字2. Lock锁3. System.Threading.Interlocked4. Monitor5. Metux6. ReaderWriterLock三、实例代码测试1. Lock锁Lock的测试总结: 一、前言   在讲述线程锁之前,我们先了解一下什么是线程同步?   线程同步----在多线程程序中,会出现多个线程抢占一个资源的...

蓝色的天空的博客 4560

C#事件多线程(二)

异步编程总结 异步编程是建立在委托基础上的一种编程方法 异步调用的每个方法都是独立的线程执行。因此。本质上就是一种多线程程序,也可以说是一种简化版本的多线程技术 比较适合在后台运行较为耗时的简单任务,并且任务要求相互独立,任务中不应该有代码直接访问可视化控件 如果后台任务要求必须按照特定顺序执行,或者必须访问公共资源,则异步编程不适合,而直接使用多线程技术 多线程的异步 public partial class Form1 : Form { ...

Cocksuck的博客 1358

C# 线程同步与对象方法访问的深度解析

本文探讨了C#多线程编程中线程访问对象同步方法的问题。通过分析不同同步机制(lock语句、MethodImpl特性)和锁粒度(方法级、对象级、自定义锁),指出其他线程能否访问对象的其他方法取决于锁对象是否相同。实验表明:不同锁对象的方法可并行执行,相同锁对象的方法会阻塞。文章还对比了各种同步机制的特点,建议遵循锁定范围最小化和时间最短化原则,推荐使用自定义对象锁实现细粒度控制。最佳实践包括避免锁住整个对象、优先使用lock语句等。

记录学习的过程 1079

C#事件多线程(一)

事件(Event) 概念 事件是对象在外界第三方因素影响下发生的事情,而对外提供的一种消息机制 事件的两个参与者 发送者(Sender):对象本身,当本身状态发生变化时,触发事件,并通知事件的接收者 接收者(Receiver):用来处理事件的,在事件发送者触发一个事件后,会自动执行的内容 事件和委托 相同点: 事件对象本质就是一个私有的委托对象,以及公有的方法,add(+=)和remove(-=) 不同点: +=方法是,实际上是调用add方法对委托进行添加。

Cocksuck的博客 4192

C#的线程同步

使用线程有几个原因。假设从应用程序中进行网络调用需要一定的时间。我们不希望用户界面停止响应,让用户一直等待直到从服务器返回一个响应。用户可以同时执行其他一些操作,或者甚至取消发送给服务器的请求。这些都可以使用线程来实现。 线程是程序中独立的指令流。使用C编写任何程序时,都有一个入口点:Main()方法。程序从Main(方法的第一条语句开始执行,直到这个方法返回为止。 这种程序结构非常适合于其中有一个可识别的任务序列的程序,但程序常常需要同时完成多个任务。线程对客户端和服务器端应用程序都非常重要。

IT技术猿猴的博客 4504

Simulink仿真避坑指南:如何设置步长、powergui和模块采样时间才能让控制周期更稳定

本文深入解析Simulink仿真中步长、powergui和模块采样时间的协同配置方法,帮助工程师解决控制周期不稳定问题。通过实际案例和参数调优技巧,指导用户精准设置仿真参数,确保电机控制、电力电子系统等工业仿真结果的可靠性。

weixin_42524762的博客 419

c# 多线程 执行事件 并发_C#.NET 最基础的线程知识

线程的知识太多,知识点有深有浅,往深的研究会涉及操作系统、CPU、内存,往浅了说就是一些语法。没有一定的知识积累,很难把线程的知识写得全面,当然我也没有这个能力。所以想到一个点写一个点,尽量总结一些有用的知识点。线程是个大话题,这个系列可能会有好几遍关于线程的,先从基础的开始,热热身。一些基础概念线程(Thread)是操作系统能够进行运算调度的最小单位。它是进程中的实际运作单位,一个进程...

weixin_39691055的博客 743

windows 下AD9361的 no-os master 工程搭建

参考网站: https://wiki.analog.com/resources/fpga/docs/build#windows_environment_setup https://wiki.analog.com/resources/eval/user-guides/ad-fmcomms2-ebz/software/no-os-setup 参考博客: https://blog.csdn.net...

Bronceyang的博客 7295

多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(下)

      同系列阅读:多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上),多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(中)       前两篇简单介绍了线程同步lock,Monitor,同步事件EventWaitHandler,互斥体Mutex的基本用法,在此基础上,我们对它们用法进行比较,并给出什么时候需要锁什么时候不需要的几...

weixin_34026484的博客 256

JSAPI微信支付(小程序)V3版本JAVA代码实战源码

1.1、商户通过JSAPI/小程序下单接口获取到发起支付的必要参数prepay_id后,再通过微信小程序调起微信支付收银台JSAPI/小程序下单_JSAPI支付|微信支付商户文档中心1.3、微信H5发起类似,结合文档,细微调整,前端返回类似,参考文档修改为微信内置浏览器调起支付即可。

java web 708

Basler相机和pylon viewer 的使用

文章目录1 线路连接2 安装和使用pylon 1 线路连接 电源线接到电源插座,网线接到电脑(电脑没有网口的话,可用转换器转到type-c接口)。 2 安装和使用pylon 参考 Basler pylon安装及对网口相机基本设置 相机和电脑以太网IP设置参考:balser相机IP设置 关于相机参数和pylon viewer的详细设置,可以参考pylon Viewer安装路径下的文档(Documentation) ...

Deephome的博客 1万+

c# 线程同步: 详解lock,monitor,同步事件和等待句柄以及mutex

来源:老刘忙不忙  http://www.cnblogs.com/xd125/archive/2007/12/12/992406.html    最近由于在准备Collection对象培训的PPT,因为涉及到SyncRoot的属性的讲解,所以对怎样在多线程应用程序中同步资源访问做了个总结:对于引用类型和非线程安全的资源的同步处理,有四种相关处理:lock关键字,监视器(Monitor), 同步

TonyWang的专栏 868

准备生日礼物

摘要: 题目要求统计某月份需要准备的员工生日礼物数量。输入包括目标月份、员工姓名列表和对应的生日列表(格式为Year/Month/Day)。需处理重复员工(以最后一次录入为准),统计该月生日的员工人数。示例1中5月有3名员工生日,输出3;示例2中10月有6名,输出6。解题关键在于使用哈希表去重(以员工姓名为键),拆分生日字符串提取月份进行匹配。代码实现(C++/Java/Python)均遵循以下步骤:读取输入、哈希去重、解析月份并统计目标月份人数。时间复杂度为O(n),空间复杂度O(n)。

面试高手的博客 300

C#线程同步的几种方法

我们在编程的时候,有时会使用多线程来解决问题,比如你的程序需要在 后台处理一大堆数据,但还要使用户界面处于可操作状态;或者你的程序需要访问一些外部资源如数据库或网络文件等。这些情况你都可以创建一个子线程去处理, 然而,多线程不可避免地会带来一个问题,就是线程同步的问题。如果这个问题处理不好,我们就会得到一些非预期的结果。在网上也看过一些关于线程同步的文章,其实线程同步有好几种方法,下面我就简单的做一下归纳。volatile是最简单的一种同步方法,当然简单是要付出代价的。

积累点滴,保持自我 8397

Python爬虫实战:如何抓取旅游论坛上的帖子、旅行攻略和评论内容

无论是知名的旅游网站,还是地方性的旅游讨论区,用户发布的旅行贴文、攻略和评论往往包含着大量的宝贵信息。本文将详细介绍如何使用Python编写一个爬虫,抓取旅游论坛上的帖子、旅行攻略和评论内容,涵盖从获取页面内容、解析数据到处理动态加载等各个方面的技术。从基本的请求和网页解析,到动态加载页面的处理,再到数据存储和反爬虫技巧,我们详细讲解了抓取旅游论坛数据的全过程。通过本文的介绍,你可以将所学的爬虫技术应用到其他类似的网站抓取任务中,为后续的数据分析、机器学习等任务提供丰富的数据源。在这种情况下,传统的。

2201_76125261的博客 1557

zabbix深度监控之邮件告警、微信群和微信告警

本文详细介绍了Zabbix监控系统实现邮件告警和微信告警的配置方法。邮件告警部分包含QQ和163邮箱的SMTP服务器配置、消息模板设置、告警流程三步骤(媒介配置、触发器动作、用户设置)。微信告警分为两种方式:企业微信群机器人告警(通过webhook实现)和企业微信应用告警(需创建应用并获取API参数)。两种告警方式都提供了完整的配置流程,包括脚本编写、权限设置、消息模板定制等关键步骤,并特别说明了如何使个人微信接收企业微信告警消息。文章还对比了SMTP、POP3、IMAP三种邮件协议的差异及适用场景。

提供C#相关开发 、云计算、运维测试、网络安全相关技术分享与服务,有意向可联系。 816

C#多线程实例汇总

我汇总了一下关于C#多线程的使用方法,包括一些简单的锁和线程池的常用方法,虽然古老了一些,但是在一些老的项目中还是能经常用到的,多线程和异步有点儿小区别,分两篇文章来汇总,下一篇我再总结一下关于C#异步的新旧使用方法。

Dahlin哥's 博客 4820

杰理 JL701N/JL703N USB MIDI 使用教程

摘要:本教程介绍如何在杰理JL701N/JL703N音箱的可视化SDK中配置USB MIDI功能。主要步骤包括:1)启用PC模式;2)修改SDK宏定义启用USB MIDI;3)实现MIDI数据接收处理逻辑;4)调用接口发送MIDI数据;5)使用MIDI-OX测试功能。针对低版本SDK可能出现的设备识别问题,提供了在midi.c文件中添加特定代码行的解决方案。使用前需确保SDK版本达标,并注意备份原始文件。

qq_42014563的博客 1268

C# 多线程(2)——线程同步

当在同一时刻多个线程操作共享资源时就会导致数据的错误,但是如果在单一线程中按照顺序就不出现这样的问题,这也就引申出线程同步的内容,保证多个线程提升性能的前提下,也不会出现程式数据的错误,重点就是让多个线程按照一定的顺序同步的执行代码,就是线程同步的概念。lock 排它锁的使用,确保了多个线程在访问竞态代码块时,只有一个线程是获得CPU时间片的,其他的线程处于阻塞中,并处于一个等待队列中。直到锁被释放,等待的线程属于先到先得的情形,依次等待获得锁去执行竞态代码块,保证了线程同步,因此可以保证线程的安全。

contact97的博客 2124

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

C# 线程间的数据同步机制及示例

C# 线程间的数据同步机制及示例

qq_35320456的博客 3015

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

上一篇: ASP.NET之Cookie(坑爹的Response.Cookies.Remove)
下一篇: 使用Lucene.Net实现全文检索
wingahi
博客等级 码龄15年 15粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值