Q5: 什么是阻塞(Blocking)和非阻塞(Non-Blocking)I/O?请举例说明。

20 阅读3分钟

Node.js 面试题详细答案 - Q5

Q5: 什么是阻塞(Blocking)和非阻塞(Non-Blocking)I/O?请举例说明。

阻塞 I/O (Blocking I/O)

阻塞 I/O 是指当程序执行 I/O 操作时,会等待操作完成才继续执行后续代码。

特点
  • 同步执行
  • 等待 I/O 完成
  • 阻塞主线程
  • 简单但效率低
示例
const fs = require('fs')

console.log('开始读取文件')

// 阻塞 I/O - 同步读取文件
try {
  const data = fs.readFileSync('large-file.txt', 'utf8')
  console.log('文件读取完成,大小:', data.length)
} catch (error) {
  console.error('读取文件失败:', error.message)
}

console.log('继续执行其他任务')

// 输出顺序:
// 开始读取文件
// 文件读取完成,大小: 1024
// 继续执行其他任务
阻塞 I/O 的问题
const fs = require('fs')

console.log('任务1开始')

// 阻塞操作 - 会阻塞后续任务
const data1 = fs.readFileSync('file1.txt')
console.log('任务1完成')

console.log('任务2开始')
const data2 = fs.readFileSync('file2.txt')
console.log('任务2完成')

console.log('任务3开始')
const data3 = fs.readFileSync('file3.txt')
console.log('任务3完成')

// 总时间 = 任务1时间 + 任务2时间 + 任务3时间
// 无法并行执行

非阻塞 I/O (Non-Blocking I/O)

非阻塞 I/O 是指当程序执行 I/O 操作时,不会等待操作完成,而是立即返回并继续执行后续代码。

特点
  • 异步执行
  • 不等待 I/O 完成
  • 不阻塞主线程
  • 复杂但效率高
示例
const fs = require('fs')

console.log('开始读取文件')

// 非阻塞 I/O - 异步读取文件
fs.readFile('large-file.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('读取文件失败:', err.message)
    return
  }
  console.log('文件读取完成,大小:', data.length)
})

console.log('继续执行其他任务')

// 输出顺序:
// 开始读取文件
// 继续执行其他任务
// 文件读取完成,大小: 1024
非阻塞 I/O 的优势
const fs = require('fs')

console.log('开始并行任务')

// 非阻塞操作 - 可以并行执行
fs.readFile('file1.txt', (err, data1) => {
  console.log('任务1完成')
})

fs.readFile('file2.txt', (err, data2) => {
  console.log('任务2完成')
})

fs.readFile('file3.txt', (err, data3) => {
  console.log('任务3完成')
})

console.log('所有任务已启动')

// 总时间 ≈ max(任务1时间, 任务2时间, 任务3时间)
// 可以并行执行

对比示例

阻塞 vs 非阻塞
const fs = require('fs')

// 阻塞方式
function blockingExample() {
  console.log('=== 阻塞方式 ===')
  const start = Date.now()

  const data1 = fs.readFileSync('file1.txt')
  const data2 = fs.readFileSync('file2.txt')
  const data3 = fs.readFileSync('file3.txt')

  const end = Date.now()
  console.log(`阻塞方式耗时: ${end - start}ms`)
}

// 非阻塞方式
function nonBlockingExample() {
  console.log('=== 非阻塞方式 ===')
  const start = Date.now()
  let completed = 0

  function checkComplete() {
    completed++
    if (completed === 3) {
      const end = Date.now()
      console.log(`非阻塞方式耗时: ${end - start}ms`)
    }
  }

  fs.readFile('file1.txt', checkComplete)
  fs.readFile('file2.txt', checkComplete)
  fs.readFile('file3.txt', checkComplete)
}

// 执行对比
blockingExample()
nonBlockingExample()

实际应用场景

HTTP 服务器对比
const http = require('http')

// 阻塞方式 - 每个请求阻塞服务器
const blockingServer = http.createServer((req, res) => {
  console.log('处理请求:', req.url)

  // 阻塞操作 - 会阻塞其他请求
  const data = fs.readFileSync('large-file.txt')
  res.end(data)
})

// 非阻塞方式 - 可以处理并发请求
const nonBlockingServer = http.createServer((req, res) => {
  console.log('处理请求:', req.url)

  // 非阻塞操作 - 不阻塞其他请求
  fs.readFile('large-file.txt', (err, data) => {
    if (err) {
      res.statusCode = 500
      res.end('Internal Server Error')
      return
    }
    res.end(data)
  })
})

// 非阻塞服务器可以同时处理多个请求
nonBlockingServer.listen(3000)
数据库操作对比
const mysql = require('mysql')

const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'test',
})

// 阻塞方式 - 同步查询
function blockingQuery() {
  console.log('开始查询')

  // 阻塞操作
  const result = connection.querySync('SELECT * FROM users')
  console.log('查询完成')

  return result
}

// 非阻塞方式 - 异步查询
function nonBlockingQuery(callback) {
  console.log('开始查询')

  // 非阻塞操作
  connection.query('SELECT * FROM users', (err, result) => {
    if (err) {
      callback(err, null)
      return
    }
    console.log('查询完成')
    callback(null, result)
  })
}

// 使用示例
nonBlockingQuery((err, result) => {
  if (err) {
    console.error('查询失败:', err)
    return
  }
  console.log('查询结果:', result)
})

现代异步编程

Promise 方式
const fs = require('fs').promises

// 使用 Promise 的非阻塞 I/O
async function modernNonBlocking() {
  console.log('开始读取文件')

  try {
    const data = await fs.readFile('large-file.txt', 'utf8')
    console.log('文件读取完成,大小:', data.length)
  } catch (error) {
    console.error('读取文件失败:', error.message)
  }

  console.log('继续执行其他任务')
}

modernNonBlocking()
并行处理
const fs = require('fs').promises

// 并行处理多个文件
async function parallelProcessing() {
  console.log('开始并行处理')

  try {
    const [data1, data2, data3] = await Promise.all([
      fs.readFile('file1.txt', 'utf8'),
      fs.readFile('file2.txt', 'utf8'),
      fs.readFile('file3.txt', 'utf8'),
    ])

    console.log('所有文件读取完成')
    console.log('文件1大小:', data1.length)
    console.log('文件2大小:', data2.length)
    console.log('文件3大小:', data3.length)
  } catch (error) {
    console.error('处理失败:', error.message)
  }
}

parallelProcessing()

总结

  • 阻塞 I/O:同步执行,等待完成,简单但效率低
  • 非阻塞 I/O:异步执行,立即返回,复杂但效率高
  • Node.js 优势:非阻塞 I/O 实现高并发
  • 现代开发:使用 Promise/async-await 简化异步编程
  • 选择原则:I/O 密集型任务使用非阻塞,CPU 密集型任务需要特殊处理