C#常见面试题

1.i++中为什么用到锁

在C#中,i++通常不需要用锁,因为i++操作本身是一个原子操作。原子操作是指一个操作要么完全执行,要么完全不执行,不会被中断。因此,在单线程环境下,i++操作是安全的。

然而,在多线程环境下,如果多个线程同时对i进行++操作,就可能会出现竞争条件(race condition),导致数据不一致或错误的结果。为了避免这种情况,需要使用锁来保护i的操作,确保在同一时刻只有一个线程可以对i进行++操作。

所以在多线程环境下,如果需要对共享的变量进行自增操作,就需要使用锁来确保操作的原子性和线程安全性。

2.多线程中原子性是指

在多线程编程中,原子性是指一个操作要么完整地执行,要么完全不执行,不会被中断。换句话说,原子操作是不可分割的,不会被其他线程中断或干扰。

在多线程环境下,多个线程可能同时访问和修改共享的数据,如果操作不是原子性的,就可能出现竞争条件(race condition),导致数据不一致或错误的结果。因此,确保关键操作的原子性对于多线程编程至关重要。

在C#中,可以使用lock关键字、Interlocked类、Monitor类或volatile关键字等机制来确保操作的原子性。此外,C#中的一些并发集合(如ConcurrentQueue、ConcurrentDictionary等)也提供了原子操作的支持,可以在多线程环境中安全地操作共享数据。

3.锁是什么,如何优化锁

在C#中,锁是一种用于控制多线程访问共享资源的机制。当多个线程需要访问共享资源时,可以使用锁来确保同一时间只有一个线程可以访问该资源,从而避免数据竞争和并发访问导致的问题。

在C#中,可以使用lock关键字来创建一个锁。lock键字用于定义一个临界区,只有一个线程可以进入该临界区,其他线程必须等待。

以下是一个简单的示例:

class Program

{

    static object lockObj = new object();

    static void Main()

    {

        // 线程1

        new Thread(DoWork).Start();

        

        // 线程2

        new Thread(DoWork).Start();

    }

    static void DoWork()

    {

        lock (lockObj)

        {

            // 问共享资源的代码

        }

    }

}

为了优化锁,可以考虑以下几点:

减小锁的范围:只在必要的代码块内使用锁,避免锁的范围过大,从而降低锁的竞争和线程等待时间。

使用ReaderWriterLock:如果有大量读取操作和少量写入操作,可以考虑使用ReaderWriterLock来实现读写分离,高并发性能。

使用并发集合:C#提供了一些并发集合类,如ConcurrentDictionary、ConcurrentQueue等,可以避免使用锁而实现线程安全的操作。

使用无锁算法:可以考虑使用无锁算法来实现并发操作,如Interlocked类的原子操作和CAS(Compare-And-Swap)算法。

使用异步编程:通过使用异步编程模型和Task Parallel Library(TPL),可以避免显式地使用锁来控制多线程访问共享资源。

4.什么是ORM框架,有哪些ORM框架

ORM(Object-Relational Mapping)框架是一种编程技术,用于在面向对象编程语言和关系型数据库之间建立映射关系,使得对象和数据库之间的交互变得更加方便和高效。ORM框架的主要目标是通过将数据库中的表和行映射为对象和属性,从而简化数据库操作,并提供一种更加面向对象的方式来处理数据。

    1 .C#中的ORM框架

    • SqlSugar (国内)
    • Dos.ORM (国内)
    • Chloe (国内)
    • StackExchange/Dapper (国外)
    • Entity Framework (EF) (国外)
    • NHibernate (国外)
    • ServiceStack/ServiceStack.OrmLite (国外)
    • linq2db (国外)
    • Massive (国外)

    5.请简述TASK状态机实现和工作原理

    CPS全称是Continuation Passing Style,在.NET中,它会自动编译为:

    将所有引用的局部变量做成闭包,放到一个隐藏的状态机的类中
    将所有的await展开成一个状态号,有几个await就有几个状态号
    每次执行完一个状态,都重复回调状态机的MoveNext方法,同时指定下一个状态号
    MoveNext方法还需处理线程和异常等问题

    6.Task和Thread区别

    Task和Thread都能创建用多线程的方式执行代码,但它们有较大的区别

    Task较新,发布于.NET 4.5,能结合新的async/await代码模型写代码,它不止能创建新线程,还能使用线程池(默认)、单线程等方式编程,在UI编程领域,Task还能自动返回UI线程上下文,还提供了许多便利API以管理多个Task

    7.lock为什么要锁定一个参数(可否为值类型?)参数有什么要求?

    lock的锁对象要求为一个引用类型,可以锁定值类型,但值类型会被装箱,每次装箱后的对象都不一样,会导致锁定无效

    对于lock锁,锁定的这个对象参数才是关键,这个参数的同步索引块指针会指向一个真正的锁(同步块),这个锁(同步块)会被复用

    你可能感兴趣的:(java,开发语言)