C# - 多线程

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

示例

using System;
using System.Threading;

public class Worker
{
    // This method will be called when the thread is started.
    public void DoWork()
    {
        while (!_shouldStop)
        {
            Console.WriteLine("worker thread: working...");
        }
        Console.WriteLine("worker thread: terminating gracefully.");
    }
    public void RequestStop()
    {
        _shouldStop = true;
    }
    // Volatile is used as hint to the compiler that this data
    // member will be accessed by multiple threads.
    private volatile bool _shouldStop;
}

public class WorkerThreadExample
{
    static void Main()
    {
        // Create the thread object. This does not start the thread.
        Worker workerObject = new Worker();
        Thread workerThread = new Thread(workerObject.DoWork);

        // Start the worker thread.
        workerThread.Start();
        Console.WriteLine("main thread: Starting worker thread...");

        // Loop until worker thread activates.
        while (!workerThread.IsAlive);

        // Put the main thread to sleep for 1 millisecond to
        // allow the worker thread to do some work:
        Thread.Sleep(1);

        // Request that the worker thread stop itself:
        workerObject.RequestStop();

        // Use the Join method to block the current thread 
        // until the object's thread terminates.
        workerThread.Join();
        Console.WriteLine("main thread: Worker thread has terminated.");
    }
}

线程同步

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

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

  一、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方法,使状态由非终止变为终止,重新激活子线程。

    参考:

    1/MSDN(http://msdn.microsoft.com/zh-cn/library/ms173179(VS.80).aspx )

    2/http://www.cnblogs.com/VincentWP/archive/2008/06/25/1229104.html




简单了解线程和协程(C#) 阅读详情

相关推荐

C# 多线程详细讲解

C# 多线程详细讲解 一、基本概念 1、进程 首先打开任务管理器,查看当前运行的进程: 从任务管理器里面可以看到当前所有正在运行的进程。那么究竟什么是进程呢? 进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程。 线程是操作系统分配处理器时间的基本单元,在进程中可以有多个线程同时执行代码。进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败

weixin_42579159的博客 1万+

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

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

weixin_33779515的博客 436

C#语言的多线程编程

线程是程序执行的最小单位,一个进程可以包含多个线程。多线程的目标是使得程序能够同时执行多个任务,提高资源的利用率和程序的效率。通过多线程,开发者可以将繁重的计算任务分配给多个线程,让它们并行运行,从而缩短整体执行时间。C#语言的多线程编程为我们提供了强大的工具,以提高应用程序的性能和响应速度。通过多个内置类(如ThreadThreadPool和Task)和同步机制(如lockMutex和Semaphore),我们能够有效地管理线程并确保数据的一致性。

2401_90032081的博客 988

C#中多线程Task详解

频繁的创建和消耗比较好资源;提供操作线程的API不是马上响应(线程是操作系统统一管理,收到指令之后,具体还得操作系统真实处理,而操作系统收到指令之后并非马上执行相关指令);

m0_48667560的博客 1万+

C#中更强大的协程实现——.NET

在C#中,.NET框架提供了Task和async/await模式来处理异步操作,但有时候我们需要更高级的协程功能来管理复杂的异步流程。总结起来,C#中的协程是一种强大的并发编程模型,可以简化异步代码的编写和管理。通过使用第三方库AsyncEnumerable,我们可以实现更强大的协程功能,并以一种直观、简洁的方式处理复杂的异步流程。通过使用AsyncEnumerable库,我们可以以一种更直观、简洁的方式编写复杂的异步代码。在这里,我们将介绍使用第三方库AsyncEnumerable来实现协程功能的方法。

RzuScala的博客 843

c#多线程

总结一下同步方法和异步方法的区别:1、同步方法由于主线程忙于计算,所以会卡住界面。异步方法由于主线程执行完了,其他计算任务交给子线程去执行,所以不会卡住界面,用户体验性好。2、同步方法由于只有一个线程在计算,所以执行速度慢。异步方法由多个线程并发运算,所以执行速度快,但并不是线性增长的(资源可能不够)。多线程也不是越多越好,只有多个独立的任务同时运行,才能加快速度。3、同步方法是有序的。异步多线程是无序的:启动无序,执行时间不确定,所以结束也是无序的。

类人_猿的博客 3540

C# 多线程

一、基本概念 1、进程 首先打开任务管理器,查看当前运行的进程: 从任务管理器里面可以看到当前所有正在运行的进程。那么究竟什么是进程呢? 进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程。线程是操作系统分配处理器时间的基本单元,在进程中可以有多个线程同时执行代码。进...

1万+

C# 多线程编程

CSharp 多线程的使用

片言可决讼,半纸能缚龙 1491

C#多线程

C#中的多线程编程是开发高效并发应用程序的关键技术之一,它允许程序同时执行多个任务,从而提升应用程序的响应速度和性能。为了更好地理解C#中的多线程使用和定义,我们可以从以下几个方面来探讨:线程的基本概念、创建线程的方法、线程的状态管理以及线程同步机制。

C6666888的博客 1570

C#线程同步的几种方法

C#线程同步的几种方法 C#线程同步的几种方法

libfacedetection 框架阅读及扩展经验

libfacedetection 框架源码解析与扩展心得1. libfacedetection 框架简介2. 源码解析2.1 facedetectcnn.h2.2 facedetectcnn.cpp2.3 facedetectcnn-model.cpp3. 其他层的扩展--depthwise convolution 前段时间因为工作需要,对libfacedetection框架做了些扩展,在阅读源...

weixin_42616332的博客 1500

C#【高级篇】 C# 多线程

线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。 线程是轻量级进程。一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了 CPU 周期的浪费,同时提高了应用程序的效率。 到目前为止我们编写的程序是一个单线程作为应用程序的运行实例的单一的过程运行的。但是,这样子应用程序同时只能执行一个任务。为了同时执行多个任务,它可以被划分为更小的线程。 ...

星Yeah 的博客 8577

基于PLC1200与CB1241的RS485从站Modbus Rtu通信实战指南

本文提供了一份详细的西门子S7-1200 PLC与CB1241 RS485通讯板实现Modbus RTU从站通信的实战指南。内容涵盖从硬件接线、博途软件组态、关键参数设置到程序调试与测试的全过程,重点解析了Modbus_Slave指令配置与数据块映射,帮助工程师快速掌握工业自动化系统中的数据采集与设备接入技术。

Apple的专栏 795

C#多线程的使用

多线程相关的命令 Thread、ThreadPool、Task、Async、Invoke、await Thread的使用方式 Thread的使用可以参考官方的连接: https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.thread?view=netframework-4.7.2 简单的线程处理功能 这里为了演示,使用wpf 的界面整合,不过还是使用的click 耦合代码进行演示: Xaml的前端代码是: <G

叮当说的博客 2169

用Cursor+Node.js零代码搭建健康数据仪表盘:从数据录入到可视化全流程

本文详细介绍了如何利用Cursor AI编程助手和Node.js,无需手动编写代码,即可构建个人健康数据仪表盘的全流程。通过自然语言指令,实现从数据录入、存储到动态图表可视化的完整系统,展示了AI驱动开发在降低技术门槛、提升开发效率方面的强大能力。

gitlab7runner的博客 699

全志平台Sensor驱动移植实战:从点亮到调优的完整指南

本文详细介绍了在全志平台上进行Sensor驱动移植的全过程,从硬件引脚配置到软件参数调优,涵盖点亮、出图和调优三大阶段。重点解析了OV02B10传感器的I2C通信建立、上电时序调试和初始化参数配置等关键技术点,并提供了实用的调试技巧与问题排查指南,帮助开发者高效完成全志平台Sensor驱动移植工作。

weixin_28727975的博客 218

C#多线程详解

上下文切换(Context Switching)是多线程环境中的一种操作,指的是在一个 CPU 核心上切换正在执行的线程,从当前线程的执行上下文(包括寄存器状态、程序计数器等)切换到另一个线程的执行上下文, 线程的切换需要额外的开销,因此在某些情况下,过多的线程可能会导致性能下降。因此,在设计多线程应用程序时,需要考虑如何减少上下文切换的发生,以提高程序的执行效率。是用于实现线程同步的一种机制,用于保护共享资源,避免竞态条件。上下文切换开销指的是从一个线程切换到另一个线程的过程中所涉及的时间和资源开销。

世界既不黑也不白,而是一道精致的灰。 4455

ThinkPad T14 Gen 2与T14P系列对比

表格您的需求推荐机型日常办公、出差、文档处理、视频会议✅(够用、轻便、便宜)视频剪辑、3D建模、编程编译、AI训练、高色准设计✅(性能强、屏幕好、扩展佳)想要未来 5 年不换机✅T14p预算低于 5000 元✅T14 Gen 2(或考虑 T14 Gen 3/4)T14p 2025 款 ≠ 小改款,而是架构级升级它从“高性能笔记本”进化为“AI 移动工作站”,核心差异在于NPU + Ultra 平台 + 全面屏显 + 超长续航。如果您从事AI 开发、视频创作、工程仿真、多任务编程。

tecoes的博客 2738

C#异步多线程快速入门

C#异步多线程快速入门 前言:

Mekeater的博客 1643

跨阻放大器(TIA)的输入输出阻抗的定义、分析和影响因素

跨阻放大器(Transimpedance Amplifier,简称TIA)是一种常用于将电流信号转换为电压信号的放大器。在TIA电路中,输入阻抗是指输入端对电流信号的响应能力,输出阻抗是指输出端对负载电阻的驱动能力。本文将分别介绍TIA的输入和输出阻抗的定义、分析以及影响因素。

weixin_44378594的博客 1万+

MIT Mini Cheetah--仿真流程

MIT Mini Cheetah–仿真 摘要 由于官方提供代码中,控制仿真环境中的猎豹机器人运动需要遥控器来控制。官方默认推荐使用logitech Gamepad F310游戏手柄:We use the Logitech F310 controller. There’s a switch in the back, which should be in the “X” position. The controller needs to reconnected if you change the switch

Galaxy_Robot的博客 5916
上一篇: Android - dip、dp、px、sp等单位说明
下一篇: C# - Log4net
G机器猫
博客等级 码龄16年 106粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值